Najlepszy sposób na zmianę koloru tła dla NSView


149

Szukam najlepszego sposobu, aby zmienić backgroundColorplik NSView. Chciałbym również móc ustawić odpowiednią alphamaskę dla NSView. Coś jak:

myView.backgroundColor = [NSColor colorWithCalibratedRed:0.227f 
                                                   green:0.251f 
                                                    blue:0.337 
                                                   alpha:0.8];

Zauważyłem, że NSWindowma tę metodę i nie jestem wielkim fanem opcji NSColorWheel, czyli NSImagetła, ale jeśli są najlepsze, chętnie z nich skorzystam.

Odpowiedzi:


134

Tak, twoja własna odpowiedź była prawidłowa. Możesz również użyć metod Cocoa:

- (void)drawRect:(NSRect)dirtyRect {
    // set any NSColor for filling, say white:
    [[NSColor whiteColor] setFill];
    NSRectFill(dirtyRect);
    [super drawRect:dirtyRect];
}

W Swift:

class MyView: NSView {

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)

        // #1d161d
        NSColor(red: 0x1d/255, green: 0x16/255, blue: 0x1d/255, alpha: 1).setFill()
        dirtyRect.fill()
    }

}

18
NSRectFillużywa NSCompositeCopydo wypełnienia, więc przebije wszystko za nim - nie będzie składać się z żadnych widoków przodków. Dla wypełnienia z partially- (lub fully-) przezroczystego koloru, należy NSRectFillUsingOperation developer.apple.com/mac/library/documentation/Cocoa/Reference/... z NSCompositeSourceOvereksploatacji.
Peter Hosey

Och, to ma sens. Wypróbowałem NSRectFill, ale przezroczystość nie działała. Przychodzę do Cocoa z Cocoa Touch i jestem zaskoczony, widząc, że pod pewnymi względami framework Cocoa Touch jest bardziej kompletny (!). Zastanawiałem się nad stworzeniem rozszerzenia klasy dla NSView, które pozwoliłoby ustawić backgroundColor, tak jak w przypadku NSWindow lub UIView, ale nie sądzę, aby można było zastąpić drawRect rozszerzeniem.
BadPirate

Przepraszam, że przegapiłem część dotyczącą przejrzystości. Tak, dotyk kakao ułatwia wiele rzeczy. Jeśli robisz również Cocoa, Twoja własna odpowiedź jest w rzeczywistości lepsza, ponieważ jest bardziej przenośna.
mohsenr

Nie rozumiem, to jest rysowanie białego koloru na górze widoku, właśnie próbowałem. Czy nie chodziło o kolor tła?
aneuryzm

@Patrick - Być może będziesz musiał wywołać super drawRect :) lub jeśli masz inne rzeczy, które mają być narysowane na tle, upewnij się, że są one po wywołaniu NSRectFill.
BadPirate

116

Łatwym i wydajnym rozwiązaniem jest skonfigurowanie widoku tak, aby używał warstwy animacji podstawowej jako magazynu zapasowego. Następnie możesz użyć, -[CALayer setBackgroundColor:]aby ustawić kolor tła warstwy.

- (void)awakeFromNib {
   self.wantsLayer = YES;  // NSView will create a CALayer automatically
}

- (BOOL)wantsUpdateLayer {
   return YES;  // Tells NSView to call `updateLayer` instead of `drawRect:`
}

- (void)updateLayer {
   self.layer.backgroundColor = [NSColor colorWithCalibratedRed:0.227f 
                                                          green:0.251f 
                                                           blue:0.337 
                                                          alpha:0.8].CGColor;
}

Otóż ​​to!


6
Uwaga: w bardzo kreatywny sposób wywołuj setLayer:lub setWantsLayer:przerywaj WebView, które możesz mieć w swojej aplikacji! (Nawet w różnych oknach ...)
rluba

1
W metodzie setWantsLayer:jest zdefiniowane: Podczas korzystania z widoków opartych na warstwach nie należy nigdy wchodzić w interakcje bezpośrednio z warstwą . Kolejność, kiedy setWantsLayer:i kiedy setLayer:jest wywoływana, jest istotna.
Stephan,

Nie powinieneś ustawiać warstwy, ale nadal możesz to osiągnąć. Zobacz ColouredView na objc.io/issues/14-mac/appkit-for-uikit-developers
Clafou

80

Jeśli jesteś miłośnikiem scenorysów, oto sposób, w jaki nie potrzebujesz żadnej linii kodu .

Dodaj NSBoxjako widok podrzędny do NSView i dostosuj ramkę NSBox tak samo, jak w przypadku NSView.

W Storyboard lub XIB zmień pozycję tytułu na Brak, typ skrzynki na niestandardowy , typ obramowania na „brak” i kolor obramowania na dowolny.

Oto zrzut ekranu:

wprowadź opis obrazu tutaj

Oto wynik:

wprowadź opis obrazu tutaj


Używanie NSBox działało świetnie dla mnie. Przełączenie na widok z obsługą warstw powodowało problemy z przerysowaniem, podczas gdy okno nie.
Henrik

4
To doskonałe i proste rozwiązanie, które powinno znaleźć się w dokumentacji
UKDataGeek

Czy to jednak koloruje trójkąty NSPopover?
Ribena

żałuję tylko, że mam tylko jeden głos za, aby udzielić tej odpowiedzi.
orion elenzil

33

Jeśli najpierw ustawisz WantsLayer na YES, możesz bezpośrednio manipulować tłem warstwy.

[self.view setWantsLayer:YES];
[self.view.layer setBackgroundColor:[[NSColor whiteColor] CGColor]];

26

Myślę, że wymyśliłem, jak to zrobić:

- (void)drawRect:(NSRect)dirtyRect {
    // Fill in background Color
    CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
    CGContextSetRGBFillColor(context, 0.227,0.251,0.337,0.8);
    CGContextFillRect(context, NSRectToCGRect(dirtyRect));
}

Dzięki. Musiałem przekonwertować dirtyRect na CGRect. Zmieniono ostatni wiersz na CGContextFillRect(context, NSRectToCGRect(dirtyRect));Czy możesz edytować swoją odpowiedź?
Ross

1
Kiedyś były bezpłatne, pomostowe: /
BadPirate

6
NSRect to kolejny sposób na powiedzenie CGRect podczas tworzenia wersji 64-bitowej lub iOS lub tworzenia wersji 32-bitowej, np. 64-bitowej. Jeśli nie, to są to struktury niezależne i nawet jeśli mogą składać się z tych samych elementów, nigdy nie mogą być „mostami bezpłatnymi”, ponieważ jest to termin odnoszący się tylko do obiektów (struktur, które mają wskaźnik „isa” i są przekazywane za pomocą wskaźników), ale nie do struktur ogólnych.
Raphael Schweikert,

użycie drawRect powoduje wydłużenie czasu rysowania itp. CALayer jest lepszym rozwiązaniem w wersjach 10.8 i nowszych.
Tom Andersen

22

Przejrzałem wszystkie te odpowiedzi i niestety żadna z nich nie zadziałała. Znalazłem jednak ten niezwykle prosty sposób, po około godzinie poszukiwań:)

myView.layer.backgroundColor = CGColorCreateGenericRGB(0, 0, 0, 0.9);

8
Zwróć uwagę, że NSViews nie zawsze mają warstwę, w takim przypadku to nic nie robi. Zobacz odpowiedź Ioretoparisi.
Kalle,

3
Spędziłem dużo czasu próbując wykonać ten kod w metodzie viewDidLoad mojego kontrolera (dla self.myCustomView). Doszedłem do wniosku, że zamiast tego powinieneś użyć metody viewWillAppear.
surfrider

21

edycja / aktualizacja: Xcode 8.3.1 • Swift 3.1

extension NSView {
    var backgroundColor: NSColor? {
        get {
            guard let color = layer?.backgroundColor else { return nil }
            return NSColor(cgColor: color)
        }
        set {
            wantsLayer = true
            layer?.backgroundColor = newValue?.cgColor
        }
    }
}

stosowanie:

let myView = NSView(frame: NSRect(x: 0, y: 0, width: 100, height: 100))
print(myView.backgroundColor ?? "none")     //  NSView's background hasn't been set yet = nil
myView.backgroundColor = .red               // set NSView's background color to red color
print(myView.backgroundColor ?? "none")
view.addSubview(myView)

Myślę, że to najczystsze rozwiązanie, ale jestem zaniepokojony stwierdzeniem, które ludzie z Objc.io złożyli tutaj: objc.io/issues/14-mac/appkit-for-uikit-developers - „... nie powinieneś próbować wchodzić w interakcje bezpośrednio z warstwami, ponieważ AppKit jest właścicielem tych warstw. ”. Nie jestem pewien, co może się nie udać. Tak czy inaczej, wdrażam to rozwiązanie we własnym kodzie.
Kevin Sliech

7

Najlepsze rozwiązanie:

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        self.wantsLayer = YES;
    }
    return self;
}

- (void)awakeFromNib
{
    float r = (rand() % 255) / 255.0f;
    float g = (rand() % 255) / 255.0f;
    float b = (rand() % 255) / 255.0f;

    if(self.layer)
    {
        CGColorRef color = CGColorCreateGenericRGB(r, g, b, 1.0f);
        self.layer.backgroundColor = color;
        CGColorRelease(color);
    }
}

Hehe. To zadziała, jeśli chcesz mieć losowy kolor tła za każdym razem, gdy uruchamiasz, choć jest to trochę bardziej skomplikowane niż robienie tego samego z draw rect (akceptowana odpowiedź)
BadPirate

Jeśli chodzi tylko o narysowanie koloru BG, to co jest konieczne, aby
zastąpić

Tak, najlepsza odpowiedź stackoverflow.com/a/2962882/285694 daje tę odpowiedź. Moje pytanie faktycznie wymagało posiadania kanału alfa, więc dałem temu sprawdzenie - stackoverflow.com/a/2962839/285694 - Technicznie to pytanie dotyczy ustawienia tła na NSView z kanałem Alpha (tak jak możesz z NSWindow) - Ale myślę, że wiele osób przyjeżdża tutaj, szukając tylko tego, jak ustawić kolor (dlatego pierwsza odpowiedź jest bardziej popularna).
BadPirate

6

W Swift:

override func drawRect(dirtyRect: NSRect) {

    NSColor.greenColor().setFill()
    NSRectFill(dirtyRect)

    super.drawRect(dirtyRect)
}

5

Użyj NSBox, który jest podklasą NSView, co pozwala nam łatwo stylizować

Szybki 3

let box = NSBox()
box.boxType = .custom
box.fillColor = NSColor.red
box.cornerRadius = 5

NSBox nie pomaga w przypadku NSPopover, ponieważ ma również część trójkątną.
AnaghSharma

5

Bez wątpienia najłatwiejszy sposób, również kompatybilny z zestawami kolorów:

Szybki :

view.setValue(NSColor.white, forKey: "backgroundColor")

Cel-C :

[view setValue: NSColor.whiteColor forKey: "backgroundColor"];

Konstruktor interfejsu :

Dodaj atrybut użytkownika backgroundColorw konstruktorze interfejsu typu NSColor.


Opiera się to na nieudokumentowanym zachowaniu i należy go unikać, jeśli nadal chcesz pracować w przyszłych wersjach AppKit.
nschmidt

3

Przetestowałem poniższe i zadziałało (w Swift):

view.wantsLayer = true
view.layer?.backgroundColor = NSColor.blackColor().colorWithAlphaComponent(0.5).CGColor

Czy po wykonaniu tej czynności umieściłeś obraz na swoim widoku? A może użyłeś NSView zamiast NSImageView?
justColbs

3

W Swift 3 możesz utworzyć rozszerzenie, aby to zrobić:

extension NSView {
    func setBackgroundColor(_ color: NSColor) {
        wantsLayer = true
        layer?.backgroundColor = color.cgColor
    }
}

// how to use
btn.setBackgroundColor(NSColor.gray)


1

Szybko możesz podklasować NSView i zrobić to

class MyView:NSView {
    required init?(coder: NSCoder) {
        super.init(coder: coder);

        self.wantsLayer = true;
        self.layer?.backgroundColor = NSColor.redColor().CGColor;
    }
}

1
Swift 4.2 Xcode 10 self.layer? .BackgroundColor = NSColor.red.cgColor
brian.clear

1

Tylko mała klasa wielokrotnego użytku (Swift 4.1)

class View: NSView {

   var backgroundColor: NSColor?

   convenience init() {
      self.init(frame: NSRect())
   }

   override func draw(_ dirtyRect: NSRect) {
      if let backgroundColor = backgroundColor {
         backgroundColor.setFill()
         dirtyRect.fill()
      } else {
         super.draw(dirtyRect)
      }
   }
}

// Usage
let view = View()
view.backgroundColor = .white

1

Wystarczy ustawić backgroundColorna warstwie (po wykonaniu podkładu warstwy widoku).

view.wantsLayer = true
view.layer?.backgroundColor = CGColor.white

0

Obsługuje to zmianę wyglądu całego systemu (włączanie lub wyłączanie trybu ciemnego), gdy aplikacja jest uruchomiona. Możesz również ustawić kolor tła w Interface Builder, jeśli najpierw ustawisz klasę widoku na BackgroundColorView.


class BackgroundColorView: NSView {
    @IBInspectable var backgroundColor: NSColor? {
        didSet { needsDisplay = true }
    }

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        wantsLayer = true
    }

    required init?(coder decoder: NSCoder) {
        super.init(coder: decoder)
        wantsLayer = true
    }

    override var wantsUpdateLayer: Bool { return true }

    override func updateLayer() {
        layer?.backgroundColor = backgroundColor?.cgColor
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.