Il modo migliore per cambiare il colore di sfondo per un NSView


149

Sto cercando il modo migliore per cambiare il backgroundColordi un NSView. Mi piacerebbe anche essere in grado di impostare la alphamaschera appropriata per il NSView. Qualcosa di simile a:

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

Ho notato che NSWindowha questo metodo e non sono un grande fan delle opzioni di background NSColorWheelo NSImage, ma se sono le migliori, disposte a usare.

Risposte:


134

Sì, la tua risposta era giusta. Puoi anche usare i metodi di cacao:

- (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
NSRectFillusa NSCompositeCopyper fare il riempimento, quindi bloccherà qualsiasi cosa dietro di esso - non si comporrà sopra nessuna vista degli antenati. Per riempimenti con un colore parzialmente (o completamente) trasparente, utilizzare NSRectFillUsingOperation developer.apple.com/mac/library/documentation/Cocoa/Reference/… con l' NSCompositeSourceOveroperazione.
Peter Hosey,

Oh, ha senso. Ho provato NSRectFill ma la trasparenza non ha funzionato. Vengo a Cocoa da Cocoa Touch e sono sorpreso di vedere che in qualche modo il framework Cocoa Touch è più completo (!). Stavo pensando di creare un'estensione di classe per NSView che ti consentisse di impostare backgroundColor come puoi fare con una NSWindow o UIView, ma non credo che tu possa sovrascrivere drawRect con un'estensione.
BadPirate,

Scusa, ho perso la parte sulla trasparenza. Sì, il tocco di cacao semplifica molte cose. Se stai anche facendo Cocoa touch, la tua risposta è in realtà migliore, poiché è più portatile.
Mohsenr,

Non capisco, questo sta disegnando il colore bianco nella parte superiore della vista, ho appena provato. Non era la domanda sul colore di sfondo?
aneuryzm,

@Patrick - Potrebbe essere necessario chiamare super drawRect :) o se hai altre cose che dovrebbero essere disegnate in cima allo sfondo, assicurati che siano dopo la chiamata NSRectFill.
BadPirate,

116

Una soluzione semplice ed efficiente consiste nel configurare la vista in modo da utilizzare un livello di animazione principale come archivio di supporto. Quindi puoi usare -[CALayer setBackgroundColor:]per impostare il colore di sfondo del livello.

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

Questo è tutto!


6
Attenzione: chiamare setLayer:o setWantsLayer:interrompere qualsiasi WebView che potresti avere nella tua applicazione in modi molto creativi! (Anche in finestre diverse ...)
rluba,

1
Nel metodo setWantsLayer:è definito: quando si utilizzano viste supportate da livelli non è mai necessario interagire direttamente con il livello . L'ordine di quando setWantsLayer:e setLayer:viene chiamato è rilevante.
Stephan,

Non dovresti impostare il livello ma puoi comunque raggiungere questo obiettivo. Vedi ColoredView su objc.io/issues/14-mac/appkit-for-uikit-developers
Clafou,

80

Se sei un amante dello storyboard, ecco un modo in cui non hai bisogno di nessuna riga di codice .

Aggiungi NSBoxcome sottoview a NSView e regola il frame di NSBox allo stesso modo con NSView.

In Storyboard o XIB cambia la posizione del titolo in Nessuno, Tipo di casella in Personalizzata , Tipo di bordo in "Nessuno" e Colore del bordo come preferisci.

Ecco uno screenshot:

inserisci qui la descrizione dell'immagine

Questo è il risultato:

inserisci qui la descrizione dell'immagine


L'uso di NSBox ha funzionato perfettamente per me. Il passaggio a una vista supportata da livelli stava causando problemi di ridisegno, mentre la casella no.
Henrik,

4
Questa è una soluzione eccellente e semplice che dovrebbe essere nei documenti
UKDataGeek il

Questo colore triangoli NSPopover però?
Ribena,

mi dispiace solo di avere un solo voto per dare questa risposta.
Orion Elenzil,

33

Se si imposta prima WantsLayer su YES, è possibile modificare direttamente lo sfondo del livello.

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

26

Penso di aver capito come farlo:

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

Grazie. Avevo bisogno di convertire dirtyRect in un CGRect. Ultima riga ammessa a CGContextFillRect(context, NSRectToCGRect(dirtyRect));Puoi modificare la tua risposta?
Ross,

1
In passato erano pedaggi gratuiti: /
BadPirate,

6
Quando si costruisce per 64-bit o iOS o si costruisce a 32-bit come 64-bit, NSRect è solo un altro modo di dire CGRect. In caso contrario, tuttavia, si tratta di strutture indipendenti e, sebbene possano essere costituite dagli stessi membri, non possono mai essere "a pedaggio gratuito" in quanto si tratta di un termine che si riferisce solo agli oggetti (strutture che hanno un puntatore "isa" e vengono passati utilizzando i puntatori), ma non alle strutture generiche.
Raphael Schweikert,

l'utilizzo di drawRect comporta un tempo di disegno aggiuntivo, ecc. CALayer è una soluzione migliore a partire da 10.8.
Tom Andersen,

22

Ho esaminato tutte queste risposte e nessuna di esse ha funzionato purtroppo per me. Tuttavia, ho trovato questo modo estremamente semplice, dopo circa un'ora di ricerche:)

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

8
Nota che NSViews non ha sempre un livello, nel qual caso questo non fa nulla. Vedi la risposta di Ioretoparisi.
Kalle,

3
Ho trascorso molto tempo cercando di eseguire questo codice nel metodo viewDidLoad del mio controller (per self.myCustomView). Ho capito che dovresti usare invece il metodo viewWillAppear.
surfrider,

21

modifica / aggiorna: 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
        }
    }
}

utilizzo:

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)

Penso che questa sia la soluzione più pulita, ma sono preoccupato per l'affermazione che la gente di Objc.io ha fatto qui: objc.io/issues/14-mac/appkit-for-uikit-developers - "... non dovresti provare a interagire con i livelli direttamente, poiché AppKit possiede quei livelli. ". Non sono sicuro di cosa potrebbe andare storto. Ad ogni modo, sto adottando questa soluzione nel mio codice.
Kevin Sliech,

7

Soluzione migliore :

- (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. Funzionerebbe se ti piacerebbe avere un colore di sfondo casuale ogni volta che lanci, anche se un po 'più complicato che fare lo stesso con il disegno (risposta accettata)
BadPirate

Se si tratta solo di disegnare un colore BG, allora qual è il necessario per sovrascrivere "drawRect:"
Nagaraj

Sì, la risposta migliore stackoverflow.com/a/2962882/285694 dà quella risposta. La mia domanda in realtà richiedeva un canale alfa, quindi ho dato a questo uno il controllo - stackoverflow.com/a/2962839/285694 - Tecnicamente questa domanda riguarda l'impostazione dello sfondo su un NSView con un canale alfa (come puoi fare con NSWindow) - Ma penso che molte persone vengano qui alla ricerca di come impostare il colore (quindi la prima risposta è più popolare).
BadPirate,

6

In Swift:

override func drawRect(dirtyRect: NSRect) {

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

    super.drawRect(dirtyRect)
}

5

Usa NSBox, che è una sottoclasse di NSView, che ci consente di modellare facilmente

Swift 3

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

NSBox non aiuta in caso di NSPopover poiché ha anche la parte triangolare.
AnaghSharma,

5

Senza dubbio il modo più semplice, compatibile anche con le risorse Color Set:

Veloce :

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

Obiettivo-C :

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

Interface Builder :

Aggiungere un attributo definito dall'utente backgroundColornel builder di interfaccia, di tipo NSColor.


Ciò si basa su comportamenti non documentati e dovrebbe essere evitato se si desidera ancora lavorare nelle versioni future di AppKit.
nschmidt,

3

Ho provato quanto segue e ha funzionato per me (in Swift):

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

Hai messo un'immagine sopra la tua vista dopo aver fatto questo? O hai usato un NSView invece di un NSImageView?
justColbs,

3

In Swift 3, puoi creare un'estensione per farlo:

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

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


1

In breve tempo puoi sottoclassare NSView e farlo

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

Solo una piccola classe riutilizzabile (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

Basta impostare backgroundColorsul livello (dopo aver eseguito il backup del livello di visualizzazione).

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

0

Ciò supporta la modifica dell'aspetto a livello di sistema (attivazione o disattivazione della modalità oscura) mentre l'applicazione è in esecuzione. È inoltre possibile impostare il colore di sfondo in Interface Builder, se si imposta prima la classe della vista su 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
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.