Der beste Weg, um die Hintergrundfarbe für eine NSView zu ändern


149

Ich bin auf der Suche nach dem besten Weg, um die Änderung backgroundColoreines NSView. Ich möchte auch in der Lage sein, die entsprechende alphaMaske für die einzustellen NSView. Etwas wie:

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

Ich stelle fest, dass NSWindowes diese Methode gibt, und ich bin kein großer Fan der NSColorWheeloder NSImageHintergrundoptionen, aber wenn sie die besten sind, bereit zu verwenden.

Antworten:


134

Ja, deine eigene Antwort war richtig. Sie können auch Kakaomethoden verwenden:

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

In 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
NSRectFillwird NSCompositeCopyzum Füllen verwendet, damit nichts dahinter steckt - es wird nicht über den Ansichten der Vorfahren zusammengesetzt. Verwenden Sie für Füllungen mit einer teilweise (oder vollständig) transparenten Farbe NSRectFillUsingOperation developer.apple.com/mac/library/documentation/Cocoa/Reference/… für den NSCompositeSourceOverVorgang.
Peter Hosey

Oh, das macht Sinn. Ich habe NSRectFill ausprobiert, aber die Transparenz hat nicht funktioniert. Ich komme von Cocoa Touch zu Cocoa und bin überrascht zu sehen, dass das Cocoa Touch-Framework in gewisser Weise vollständiger (!) Ist. Ich habe überlegt, eine Klassenerweiterung für NSView zu erstellen, mit der Sie backgroundColor wie ein NSWindow oder UIView festlegen können, aber ich glaube nicht, dass Sie drawRect mit einer Erweiterung überschreiben können.
BadPirate

Entschuldigung, ich habe den Transparenzteil verpasst. Ja, Cocoa Touch macht viele Dinge einfacher. Wenn Sie auch Cocoa Touch machen, ist Ihre eigene Antwort tatsächlich besser, da sie portabler ist.
Mohsenr

Ich verstehe es nicht, dies zeichnet die weiße Farbe über die Ansicht, ich habe es gerade versucht. War die Frage nicht nach der Hintergrundfarbe?
aneuryzm

@Patrick - Möglicherweise müssen Sie super drawRect aufrufen :) oder wenn Sie andere Dinge haben, die über dem Hintergrund gezeichnet werden sollen, stellen Sie sicher, dass sie nach dem Aufruf von NSRectFill liegen.
BadPirate

116

Eine einfache und effiziente Lösung besteht darin, die Ansicht so zu konfigurieren, dass eine Kernanimationsebene als Hintergrundspeicher verwendet wird. Anschließend können Sie -[CALayer setBackgroundColor:]die Hintergrundfarbe der Ebene festlegen.

- (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;
}

Das ist es!


6
Vorsicht: Aufrufen setLayer:oder setWantsLayer:Unterbrechen von WebViews in Ihrer Anwendung auf sehr kreative Weise! (Auch in verschiedenen Fenstern ...)
Rluba

1
In der Methode setWantsLayer:wird definiert: Wenn Sie Ansichten mit Ebenenunterstützung verwenden, sollten Sie niemals direkt mit der Ebene interagieren . Die Reihenfolge, wann setWantsLayer:und setLayer:aufgerufen wird, ist relevant.
Stephan

Sie sollten die Ebene nicht festlegen, können dies aber dennoch erreichen. Siehe ColoredView unter objc.io/issues/14-mac/appkit-for-uikit-developers
Clafou

80

Wenn Sie ein Storyboard-Liebhaber sind, benötigen Sie auf diese Weise keine Codezeile .

Fügen NSBoxSie NSView als Unteransicht hinzu und passen Sie den Rahmen von NSBox wie bei NSView an.

Ändern Sie in Storyboard oder XIB die Titelposition in Keine, den Feldtyp in Benutzerdefiniert , den Randtyp in "Keine" und die Rahmenfarbe nach Belieben.

Hier ist ein Screenshot:

Geben Sie hier die Bildbeschreibung ein

Das ist das Ergebnis:

Geben Sie hier die Bildbeschreibung ein


Die Verwendung von NSBox hat bei mir hervorragend funktioniert. Das Wechseln zu einer Ansicht mit Ebenenunterstützung verursachte Probleme beim erneuten Zeichnen, die Box jedoch nicht.
Henrik

4
Dies ist eine ausgezeichnete und einfache Lösung, die in den Dokumenten enthalten sein sollte
UKDataGeek

Färbt diese NSPopover-Dreiecke?
Ribena

Ich bedauere nur, dass ich nur eine Gegenstimme habe, um diese Antwort zu geben.
Orion Elenzil

33

Wenn Sie WantsLayer zuerst auf YES setzen, können Sie den Ebenenhintergrund direkt bearbeiten.

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

26

Ich glaube, ich habe herausgefunden, wie es geht:

- (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));
}

Vielen Dank. Ich musste den dirtyRect in einen CGRect konvertieren. Letzte Zeile geändert zu CGContextFillRect(context, NSRectToCGRect(dirtyRect));Können Sie Ihre Antwort bearbeiten?
Ross

1
Früher waren sie gebührenfrei überbrückt: /
BadPirate

6
Wenn Sie für 64-Bit oder iOS oder 32-Bit wie 64-Bit erstellen, ist NSRect nur eine andere Art, CGRect zu sagen. Wenn nicht, handelt es sich jedoch um unabhängige Strukturen, und obwohl sie aus denselben Mitgliedern bestehen können, können sie niemals „gebührenfrei überbrückt“ werden, da dies ein Begriff ist, der sich nur auf Objekte bezieht (Strukturen, die einen „isa“ -Zeiger haben und werden mit Zeigern herumgereicht), jedoch nicht an generische Strukturen.
Raphael Schweikert

Die Verwendung von drawRect führt zu einer zusätzlichen Zeichenzeit usw. CALayer ist eine bessere Lösung ab 10.8.
Tom Andersen

22

Ich habe all diese Antworten durchgesehen und leider hat keine für mich funktioniert. Nach ungefähr einer Stunde Suche fand ich diesen extrem einfachen Weg jedoch :)

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

8
Beachten Sie, dass NSViews nicht immer eine Ebene haben. In diesem Fall führt dies zu nichts. Siehe Ioretoparisis Antwort.
Kalle

3
Ich habe viel Zeit damit verbracht, diesen Code in der viewDidLoad-Methode meines Controllers (für self.myCustomView) auszuführen. Ich habe herausgefunden, dass Sie stattdessen die viewWillAppear-Methode verwenden sollten.
Surfrider

21

Bearbeiten / Aktualisieren: 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
        }
    }
}

Verwendung:

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)

Ich denke, dies ist die sauberste Lösung, aber ich bin besorgt über die Aussage, die die Objc.io-Leute hier gemacht haben: objc.io/issues/14-mac/appkit-for-uikit-developers - "... Sie sollten nicht versuchen, zu interagieren mit den Ebenen direkt, da AppKit diese Ebenen besitzt. " Ich bin mir nicht sicher, was schief gehen könnte. In jedem Fall übernehme ich diese Lösung in meinem eigenen Code.
Kevin Sliech

7

Beste Lösung :

- (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. Das würde funktionieren, wenn Sie bei jedem Start eine zufällige Hintergrundfarbe haben möchten, obwohl dies etwas komplizierter ist, als wenn Sie dasselbe mit draw rect (akzeptierte Antwort)
tun

Wenn es nur darum geht, eine Hintergrundfarbe zu zeichnen, was ist dann notwendig, um das "drawRect:"
Nagaraj

Ja, die Top-Antwort stackoverflow.com/a/2962882/285694 gibt diese Antwort. Meine Frage erforderte tatsächlich einen Alphakanal, daher gab ich diesem die Prüfung - stackoverflow.com/a/2962839/285694 - Technisch gesehen geht es bei dieser Frage darum, den Hintergrund in einem NSView mit einem Alphakanal festzulegen (wie Sie es mit NSWindow können). - Aber ich denke, viele Leute kommen hierher, um herauszufinden, wie man die Farbe einstellt (daher ist die erste Antwort populärer).
BadPirate

6

In Swift:

override func drawRect(dirtyRect: NSRect) {

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

    super.drawRect(dirtyRect)
}

5

Verwenden Sie NSBox, eine Unterklasse von NSView, mit der wir problemlos stylen können

Swift 3

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

NSBox hilft bei NSPopover nicht, da es auch den Dreiecksteil hat.
AnaghSharma

5

Ohne Zweifel der einfachste Weg, auch kompatibel mit Color Set Assets:

Swift :

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

Ziel-C :

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

Interface Builder :

Fügen Sie backgroundColorim Interface Builder ein benutzerdefiniertes Attribut vom Typ hinzu NSColor.


Dies beruht auf undokumentiertem Verhalten und sollte vermieden werden, wenn Sie in zukünftigen Versionen von AppKit noch arbeiten möchten.
nschmidt

3

Ich habe Folgendes getestet und es hat bei mir funktioniert (in Swift):

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

Haben Sie danach ein Bild über Ihre Ansicht gelegt? Oder haben Sie eine NSView anstelle einer NSImageView verwendet?
justColbs

3

In Swift 3 können Sie dazu eine Erweiterung erstellen:

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

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


1

In Swift können Sie NSView unterordnen und dies tun

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

Nur kleine wiederverwendbare Klasse (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

Legen Sie einfach backgroundColordie Ebene fest (nachdem Sie die Ansichtsebene gesichert haben).

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

0

Dies unterstützt das Ändern des systemweiten Erscheinungsbilds (Ein- oder Ausschalten des Dunkelmodus), während die Anwendung ausgeführt wird. Sie können die Hintergrundfarbe auch im Interface Builder festlegen, wenn Sie die Klasse der Ansicht zuerst auf BackgroundColorView festlegen.


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
    }
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.