Melhor maneira de alterar a cor de fundo de um NSView

149

Eu estou procurando a melhor maneira de mudar o backgroundColorde um NSView. Eu também gostaria de poder definir a alphamáscara apropriada para o arquivo NSView. Algo como:

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

Percebo que NSWindowtem esse método, e não sou um grande fã das opções de plano de fundo NSColorWheelou NSImage, mas se elas são as melhores, estão dispostas a usar.

BadPirate
fonte

Respostas:

134

Sim, sua própria resposta estava certa. Você também pode usar os métodos de cacau:

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

Em 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
fonte
18
NSRectFillusa NSCompositeCopypara fazer o preenchimento, de modo a impedir que tudo ocorra por trás dele - ele não será composto sobre as visualizações de ancestrais. Para preenchimentos com cores parcialmente (ou totalmente) transparentes, use NSRectFillUsingOperation developer.apple.com/mac/library/documentation/Cocoa/Reference/… com a NSCompositeSourceOveroperação.
Peter Hosey
Oh, isso faz sentido. Eu tentei o NSRectFill, mas a transparência não funcionou. Estou vindo do Cocoa Touch para Cocoa e fiquei surpreso ao ver que, de certa forma, a estrutura do Cocoa Touch é mais completa (!). Eu estava pensando em fazer uma extensão de classe para o NSView que permitisse definir backgroundColor como você pode usar para um NSWindow ou UIView, mas não acho que você possa substituir o drawRect por uma extensão.
precisa saber é o seguinte
Desculpe, perdi a parte da transparência. Sim, o toque de cacau facilita muitas coisas. Se você também está usando o Cocoa touch, sua própria resposta é realmente melhor, pois é mais portátil.
mohsenr
Eu não entendi, isso está desenhando a cor branca no topo da visualização, eu apenas tentei. Não era a pergunta sobre a cor de fundo?
Aneuryzm
@ Patrick - Você pode precisar chamar super drawRect :) ou, se tiver outras coisas que devem ser desenhadas no topo do plano de fundo, verifique se elas estão após a chamada NSRectFill.
BadPirate
116

Uma solução fácil e eficiente é configurar a exibição para usar uma camada Core Animation como armazenamento de suporte. Em seguida, você pode usar -[CALayer setBackgroundColor:]para definir a cor de fundo da camada.

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

É isso aí!

Loretoparisi
fonte
6
Cuidado: chamar setLayer:ou setWantsLayer:interromper qualquer WebViews que você possa ter em seu aplicativo de maneiras muito criativas! (Mesmo em diferentes janelas ...)
rluba
1
No método setWantsLayer:está definido: Ao usar vistas apoiadas por camada, você nunca deve interagir diretamente com a camada . A ordem de quando setWantsLayer:e setLayer:é chamado é relevante.
Stephan
Você não deve definir a camada, mas ainda pode conseguir isso. Veja o ColoredView em objc.io/issues/14-mac/appkit-for-uikit-developers
Clafou
80

Se você é um amante de storyboard, aqui está uma maneira de não precisar de nenhuma linha de código .

Adicione NSBoxcomo uma subvisão ao NSView e ajuste o quadro do NSBox da mesma forma com o NSView.

No Storyboard ou no XIB, altere a posição do Título para Nenhum, Tipo de caixa para Personalizado , Tipo de borda para "Nenhum" e Cor da borda para o que você quiser.

Aqui está uma captura de tela:

insira a descrição da imagem aqui

Este é o resultado:

insira a descrição da imagem aqui

JZAU
fonte
Usar o NSBox funcionou muito bem para mim. Mudar para uma exibição com suporte de camada estava causando problemas de redesenho, enquanto a caixa não.
Henrik
4
Esta é uma solução excelente e simples que deve estar nos documentos #
UKDataGeek 27/16/16
Mas isso cor triângulos NSPopover?
Ribena
lamento apenas que tenha apenas um voto positivo para dar essa resposta.
orion elenzil 19/09/19
33

Se você definir WantsLayer como YES primeiro, poderá manipular diretamente o plano de fundo da camada.

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

Acho que descobri como fazê-lo:

- (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
fonte
Obrigado. Eu precisava converter o dirtyRect em um CGRect. Última linha alterada para CGContextFillRect(context, NSRectToCGRect(dirtyRect));Você pode editar sua resposta?
Ross
1
Eles costumavam ser pontes gratuitas: /
BadPirate
6
Ao criar para 64 bits ou iOS ou 32 bits como 64 bits, o NSRect é apenas outra maneira de dizer CGRect. Se não, no entanto, essas são estruturas independentes e, embora possam consistir nos mesmos membros, elas nunca podem ser "pontes gratuitas", pois esse é um termo que se refere apenas a objetos (estruturas que possuem um ponteiro "isa" e são passados ​​usando ponteiros), mas não para estruturas genéricas.
Raphael Schweikert
o uso do drawRect resulta em um tempo adicional de desenho, etc. O CALayer é uma solução melhor na versão 10.8 e posterior.
21815 Tom Andersen #
22

Passei por todas essas respostas e nenhuma delas funcionou para mim, infelizmente. No entanto, achei essa maneira extremamente simples, após cerca de uma hora de pesquisa:)

myView.layer.backgroundColor = CGColorCreateGenericRGB(0, 0, 0, 0.9);
estufa
fonte
8
Observe que o NSViews nem sempre tem uma camada; nesse caso, isso não faz nada. Veja a resposta de Ioretoparisi.
Kalle
3
Passei muito tempo tentando executar esse código no método viewDidLoad do meu controlador (para self.myCustomView). Eu descobri que você deveria usar o método viewWillAppear.
precisa saber é o seguinte
21

editar / atualizar: 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
fonte
Eu acho que essa é a solução mais limpa, mas estou preocupado com a declaração feita pelo pessoal do Objc.io aqui: objc.io/issues/14-mac/appkit-for-uikit-developers - "... você não deve tentar interagir diretamente com as camadas, pois o AppKit é o proprietário dessas camadas. " Não tenho certeza do que pode dar errado. De qualquer forma, estou adotando esta solução no meu próprio código.
Kevin Sliech
7

Melhor solução :

- (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
fonte
Ele Ele. Isso iria funcionar se você gostaria de ter uma cor de fundo aleatória a cada vez que você iniciar, embora um pouco mais complicado, em seguida, fazendo o mesmo com rect empate (resposta aceita)
BadPirate
Se é apenas uma questão de desenhar uma cor BG, então o que é necessário para substituir o "drawRect:"
Nagaraj
Sim, a resposta principal stackoverflow.com/a/2962882/285694 fornece essa resposta. Minha pergunta realmente precisava ter um canal alfa, então dei a este o cheque - stackoverflow.com/a/2962839/285694 - Tecnicamente, essa pergunta é sobre definir o plano de fundo em um NSView com um canal Alpha (como você pode com o NSWindow) - Mas acho que muitas pessoas vêm aqui procurando exatamente como definir a cor (portanto, a primeira resposta é mais popular).
precisa saber é o seguinte
6

Em Swift:

override func drawRect(dirtyRect: NSRect) {

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

    super.drawRect(dirtyRect)
}
Ixx
fonte
5

Use o NSBox, que é uma subclasse do NSView, permitindo estilizar facilmente

Swift 3

let box = NSBox()
box.boxType = .custom
box.fillColor = NSColor.red
box.cornerRadius = 5
onmyway133
fonte
O NSBox não ajuda no caso do NSPopover, pois também possui a parte do triângulo.
AnaghSharma 12/0318
5

Sem dúvida a maneira mais fácil, também compatível com os ativos de conjunto de cores:

Swift :

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

Objetivo-C :

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

Construtor de interface :

Adicionar um atributo definido pelo usuário backgroundColorno construtor de interface, do tipo NSColor.

Ely
fonte
Isso depende de comportamento não documentado e deve ser evitado se você ainda deseja trabalhar em versões futuras do AppKit.
nschmidt 3/07
3

Testei o seguinte e funcionou para mim (no Swift):

view.wantsLayer = true
view.layer?.backgroundColor = NSColor.blackColor().colorWithAlphaComponent(0.5).CGColor
Tyler Long
fonte
Você colocou uma imagem em cima da visualização depois de fazer isso? Ou você usou um NSView em vez de um NSImageView?
precisa saber é o seguinte
3

No Swift 3, você pode criar uma extensão para fazer isso:

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

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

Dê uma olhada no RMSkinnedView . Você pode definir a cor de fundo do NSView no Interface Builder.

Raffael
fonte
1

No Swift, você pode subclassificar o NSView e fazer isso

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

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

Apenas uma pequena classe reutilizável (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
fonte
1

Basta definir backgroundColorna camada (depois de fazer a camada de visualização recuar).

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

Isso suporta a alteração da aparência em todo o sistema (ativar ou desativar o modo escuro) enquanto o aplicativo está em execução. Você também pode definir a cor do plano de fundo no Interface Builder, se definir a classe da exibição como BackgroundColorView primeiro.


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
fonte