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.