La mejor manera de cambiar el color de fondo para un NSView

149

Estoy buscando la mejor manera de cambiar el backgroundColorde un NSView. También me gustaría poder establecer la alphamáscara adecuada para NSView. Algo como:

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

Noto que NSWindowtiene este método, y no soy un gran fan de los NSColorWheelu NSImageopciones de fondo, pero si son los mejores, dispuestos a utilizar.

BadPirate
fuente

Respuestas:

134

Sí, tu propia respuesta fue correcta. También puede usar los métodos de cacao:

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

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

}
mohsenr
fuente
18
NSRectFillutiliza NSCompositeCopypara hacer el relleno, por lo que golpeará cualquier cosa detrás de él, no se compondrá en la parte superior de las vistas de antepasados. Para rellenos con un color transparente parcial (o totalmente), use NSRectFillUsingOperation developer.apple.com/mac/library/documentation/Cocoa/Reference/… con la NSCompositeSourceOveroperación.
Peter Hosey
Oh eso tiene sentido. Intenté NSRectFill pero la transparencia no funcionó. Estoy llegando a Cocoa desde Cocoa Touch, y me sorprende ver que, de alguna manera, el marco de Cocoa Touch es más completo (!). Estaba pensando en hacer una extensión de clase para NSView que le permitiera establecer backgroundColor como si fuera un NSWindow o UIView, pero no creo que pueda anular drawRect con una extensión.
BadPirate
Lo siento, me perdí la parte de transparencia. Sí, el toque de cacao hace que muchas cosas sean más fáciles. Si también estás haciendo Cocoa touch, tu propia respuesta es realmente mejor, ya que es más portátil.
mohsenr
No lo entiendo, esto es dibujar el color blanco en la parte superior de la vista, acabo de intentarlo. ¿No era la pregunta sobre el color de fondo?
aneuryzm
@Patrick: es posible que deba llamar a super drawRect :) o si tiene otras cosas que se supone que deben dibujarse sobre el fondo, asegúrese de que estén después de la llamada NSRectFill.
BadPirate
116

Una solución fácil y eficiente es configurar la vista para usar una capa Core Animation como su almacén de respaldo. Luego puede usar -[CALayer setBackgroundColor:]para establecer el color de fondo de la capa.

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

¡Eso es!

loretoparisi
fuente
66
Cuidado: ¡llama setLayer:o setWantsLayer:rompe cualquier WebView que tenga en su aplicación de maneras muy creativas! (Incluso en diferentes ventanas ...)
rluba
1
En el método setWantsLayer:se define: al usar vistas respaldadas por capas, nunca debe interactuar directamente con la capa . El orden de cuándo setWantsLayer:y setLayer:se llama es relevante.
Stephan
No debe configurar la capa, pero aún puede lograr esto. Ver ColouredView en objc.io/issues/14-mac/appkit-for-uikit-developers
Clafou
80

Si eres un amante del guión gráfico, esta es una forma en que no necesitas ninguna línea de código .

Añadir NSBox como una subvista a NSView y ajuste el marco de NSBox de la misma manera con NSView.

En Storyboard o XIB, cambie la posición del título a Ninguno, el tipo de cuadro a Personalizado , el Tipo de a "Ninguno" y el Color del borde a lo que desee.

Aquí hay una captura de pantalla:

ingrese la descripción de la imagen aquí

Este es el resultado:

ingrese la descripción de la imagen aquí

Jzau
fuente
Usar NSBox funcionó muy bien para mí. Cambiar a una vista respaldada por capas estaba causando problemas de redibujado, mientras que el cuadro no.
Henrik
44
Esta es una solución excelente y simple que debería estar en los documentos
UKDataGeek
Sin embargo, ¿este color de triángulos NSPopover?
Ribena
Solo lamento haber recibido un solo voto para dar esta respuesta.
orion elenzil
33

Si primero estableceWantsLayer en YES, puede manipular directamente el fondo de la capa.

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

Creo que descubrí cómo hacerlo:

- (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));
}
BadPirate
fuente
Gracias. Necesitaba convertir el dirtyRect a un CGRect. Última línea enmendada a CGContextFillRect(context, NSRectToCGRect(dirtyRect));¿Puedes editar tu respuesta?
Ross
1
Solían ser un puente gratuito: /
BadPirate
66
Cuando compila para 64 bits o iOS o construye 32 bits como 64 bits, NSRect es solo otra forma de decir CGRect. Sin embargo, de lo contrario, se trata de estructuras independientes, y aunque pueden estar formadas por los mismos miembros, nunca pueden ser "puenteadas sin cargo", ya que es un término que se refiere solo a objetos (estructuras que tienen un puntero "isa"). y se pasan utilizando punteros), pero no a estructuras genéricas.
Raphael Schweikert
el uso de drawRect da como resultado un tiempo de extracción adicional, etc. CALayer es una mejor solución en 10.8 y posteriores.
Tom Andersen
22

Revisé todas estas respuestas y ninguna de ellas funcionó para mí desafortunadamente. Sin embargo, encontré esta forma extremadamente simple, después de aproximadamente una hora de búsqueda :)

myView.layer.backgroundColor = CGColorCreateGenericRGB(0, 0, 0, 0.9);
invernadero
fuente
8
Tenga en cuenta que los NSView no siempre tienen una capa, en cuyo caso esto no hace nada. Ver la respuesta de Ioretoparisi.
Kalle
3
Pasé mucho tiempo tratando de realizar este código en el método viewDidLoad de mi controlador (para self.myCustomView). Descubrí que deberías usar el método viewWillAppear en su lugar.
surfrider
21

editar / actualizar: 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
        }
    }
}

uso:

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)
Leo Dabus
fuente
Creo que esta es la solución más limpia, pero estoy preocupado por la declaración que la gente de Objc.io hizo aquí: objc.io/issues/14-mac/appkit-for-uikit-developers - "... no debe intentar interactuar con las capas directamente, ya que AppKit posee esas capas ". No estoy seguro de qué podría salir mal. De cualquier manera, estoy adoptando esta solución en mi propio código.
Kevin Sliech
7

Mejor solución :

- (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);
    }
}
Nagaraj
fuente
Jeje. Eso funcionaría si desea tener un color de fondo aleatorio cada vez que inicie, aunque un poco más complicado que hacer lo mismo con draw rect (respuesta aceptada)
BadPirate
Si solo se trata de dibujar un color BG, entonces, ¿qué es lo necesario para anular el "drawRect:"
Nagaraj
Sí, la respuesta principal stackoverflow.com/a/2962882/285694 da esa respuesta. Mi pregunta realmente se requiere para tener un canal alfa, así que me dio éste el cheque - stackoverflow.com/a/2962839/285694 - Técnicamente esta pregunta es acerca de la configuración del fondo en una NSView con un canal alfa (como se hace con NSWindow) - Pero creo que mucha gente viene aquí buscando cómo configurar el color (por lo tanto, la primera respuesta es más popular).
BadPirate
6

En Swift:

override func drawRect(dirtyRect: NSRect) {

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

    super.drawRect(dirtyRect)
}
Ixx
fuente
5

Use NSBox, que es una subclase de NSView, que nos permite diseñar fácilmente

Swift 3

let box = NSBox()
box.boxType = .custom
box.fillColor = NSColor.red
box.cornerRadius = 5
onmyway133
fuente
NSBox no ayuda en caso de NSPopover ya que también tiene la parte del triángulo.
AnaghSharma
5

Sin duda la forma más fácil, también compatible con los Activos de conjunto de colores:

Rápido :

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

Objetivo-C :

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

Interface Builder :

Agregar un atributo definido por el usuario backgroundColor en el generador de interfaces, de tipo NSColor.

Ely
fuente
Esto se basa en el comportamiento indocumentado y debe evitarse si aún desea trabajar en futuras versiones de AppKit.
nschmidt
3

Probé lo siguiente y funcionó para mí (en Swift):

view.wantsLayer = true
view.layer?.backgroundColor = NSColor.blackColor().colorWithAlphaComponent(0.5).CGColor
Tyler Long
fuente
¿Pusiste una imagen encima de tu vista después de hacer esto? ¿O usó un NSView en lugar de un NSImageView?
justColbs
3

En Swift 3, puede crear una extensión para hacerlo:

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

// how to use
btn.setBackgroundColor(NSColor.gray)
Kaiyuan Xu
fuente
1

Echa un vistazo a RMSkinnedView . Puede establecer el color de fondo de NSView desde Interface Builder.

Raffael
fuente
1

En Swift puedes subclasificar NSView y hacer esto

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

        self.wantsLayer = true;
        self.layer?.backgroundColor = NSColor.redColor().CGColor;
    }
}
Chad Scira
fuente
1
Swift 4.2 Xcode 10 self.layer? .BackgroundColor = NSColor.red.cgColor
brian.clear
1

Solo pequeña clase reutilizable (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
Vlad
fuente
1

Simplemente configúrelo backgroundColoren la capa (después de respaldar la capa de vista).

view.wantsLayer = true
view.layer?.backgroundColor = CGColor.white
Geri Borbás
fuente
0

Esto admite cambiar la apariencia de todo el sistema (activar o desactivar el modo oscuro) mientras se ejecuta la aplicación. También puede establecer el color de fondo en Interface Builder, si primero establece la clase de la vista en 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
    }
}
kareman
fuente