Meilleur moyen de changer la couleur d'arrière-plan d'un NSView

149

Je recherche la meilleure façon de changer le backgroundColorfichier NSView. J'aimerais également pouvoir définir le alphamasque approprié pour le NSView. Quelque chose comme:

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

Je remarque que NSWindowcette méthode a cette méthode, et je ne suis pas un grand fan des options d'arrière NSColorWheel- NSImageplan, mais si elles sont les meilleures, je suis prêt à les utiliser.

BadPirate
la source

Réponses:

134

Ouais, ta propre réponse était juste. Vous pouvez également utiliser les méthodes Cocoa:

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

Dans 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
la source
18
NSRectFillutilise NSCompositeCopypour faire le remplissage, donc il écrasera tout ce qui se trouve derrière - il ne composera pas au-dessus des vues ancêtres. Pour les remplissages avec une couleur partiellement (ou entièrement) transparente, utilisez NSRectFillUsingOperation developer.apple.com/mac/library/documentation/Cocoa/Reference/… avec l' NSCompositeSourceOveropération.
Peter Hosey
Oh, cela a du sens. J'ai essayé NSRectFill mais la transparence n'a pas fonctionné. J'arrive à Cocoa depuis Cocoa Touch et je suis surpris de voir qu'à certains égards, le framework Cocoa Touch est plus complet (!). Je pensais créer une extension de classe pour NSView qui vous permettrait de définir backgroundColor comme vous pouvez un NSWindow ou UIView, mais je ne pense pas que vous puissiez remplacer drawRect avec une extension.
BadPirate
Désolé j'ai manqué la partie transparence. Ouais, Cocoa touch facilite beaucoup de choses. Si vous utilisez également Cocoa Touch, votre propre réponse est en fait meilleure, car elle est plus portable.
mohsenr
Je ne comprends pas, cela dessine la couleur blanche au-dessus de la vue, je viens d'essayer. N'était-ce pas la question de la couleur de fond?
aneuryzm
@Patrick - Vous devrez peut-être appeler super drawRect :) ou si vous avez d'autres choses qui sont censées être dessinées en arrière-plan, assurez-vous qu'elles sont après l'appel NSRectFill.
BadPirate
116

Une solution simple et efficace consiste à configurer la vue pour utiliser une couche Core Animation comme magasin de stockage. Ensuite, vous pouvez utiliser -[CALayer setBackgroundColor:]pour définir la couleur d'arrière-plan du calque.

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

C'est tout!

Loretoparisi
la source
6
Attention: appeler setLayer:ou setWantsLayer:interrompre les WebViews que vous pourriez avoir dans votre application de manière très créative! (Même dans différentes fenêtres ...)
rluba
1
Dans la méthode setWantsLayer:est définie: Lorsque vous utilisez des vues basées sur des couches, vous ne devez jamais interagir directement avec la couche . L'ordre dans lequel setWantsLayer:et setLayer:est appelé est pertinent.
Stephan
Vous ne devez pas définir le calque, mais vous pouvez toujours y parvenir. Voir ColoredView à objc.io/issues/14-mac/appkit-for-uikit-developers
Clafou
80

Si vous êtes un amateur de storyboard, voici un moyen de ne pas avoir besoin de ligne de code .

Ajoutez NSBoxcomme sous-vue à NSView et ajustez le cadre de NSBox comme le même avec NSView.

Dans Storyboard ou XIB, changez la position du titre sur Aucun, le type de boîte sur Personnalisé , le Type de bordure sur «Aucun» et la couleur de la bordure sur ce que vous voulez.

Voici une capture d'écran:

entrez la description de l'image ici

Voici le résultat:

entrez la description de l'image ici

JZAU
la source
Utiliser NSBox a très bien fonctionné pour moi. Le passage à une vue basée sur les calques provoquait des problèmes de rafraîchissement, contrairement à la boîte.
Henrik
4
C'est une solution excellente et simple qui devrait être dans la documentation
UKDataGeek
Est-ce que cette couleur des triangles NSPopover?
Ribena
Je regrette seulement de n'avoir qu'une seule voix pour donner cette réponse.
orion elenzil le
33

Si vous définissez d'abord WantsLayer sur YES, vous pouvez directement manipuler l'arrière-plan du calque.

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

Je pense avoir trouvé comment le faire:

- (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
la source
Merci. J'avais besoin de convertir le dirtyRect en CGRect. Dernière ligne modifiée: CGContextFillRect(context, NSRectToCGRect(dirtyRect));Pouvez-vous modifier votre réponse?
Ross
1
Autrefois
6
Lors de la construction pour 64 bits ou iOS ou pour la construction 32 bits comme 64 bits, NSRect est juste une autre façon de dire CGRect. Sinon, cependant, ce sont des structures indépendantes, et même si elles peuvent être constituées des mêmes membres, elles ne peuvent jamais être "pontées sans frais" car c'est un terme faisant référence uniquement aux objets (structures qui ont un pointeur "isa" et sont transmis à l'aide de pointeurs), mais pas vers des structures génériques.
Raphael Schweikert le
l'utilisation de drawRect entraîne un temps de tirage supplémentaire, etc. CALayer est une meilleure solution en 10.8 et plus.
Tom Andersen
22

J'ai parcouru toutes ces réponses et aucune d'elles n'a fonctionné pour moi, malheureusement. Cependant, j'ai trouvé ce moyen extrêmement simple, après environ une heure de recherche :)

myView.layer.backgroundColor = CGColorCreateGenericRGB(0, 0, 0, 0.9);
serre
la source
8
Notez que les NSViews n'ont pas toujours de couche, auquel cas cela ne fait rien. Voir la réponse de Ioretoparisi.
Kalle
3
J'ai passé beaucoup de temps à essayer d'exécuter ce code dans la méthode viewDidLoad de mon contrôleur (pour self.myCustomView). J'ai compris que vous devriez utiliser la méthode viewWillAppear à la place.
surfrider
21

modifier / mettre à jour: 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
        }
    }
}

usage:

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
la source
Je pense que c'est la solution la plus propre, mais je suis préoccupé par la déclaration faite ici par les gens d' Objc.io : objc.io/issues/14-mac/appkit-for-uikit-developers - "... vous ne devriez pas essayer d'interagir directement avec les calques, car AppKit possède ces calques. ". Je ne sais pas ce qui pourrait mal tourner. Quoi qu'il en soit, j'adopte cette solution dans mon propre code.
Kevin Sliech
7

Meilleure solution :

- (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
la source
Hehe. Cela fonctionnerait si vous aimez avoir une couleur d'arrière-plan aléatoire à chaque lancement, bien qu'un peu plus compliqué que de faire de même avec draw rect (réponse acceptée)
BadPirate
S'il s'agit simplement de dessiner une couleur BG, que faut-il pour remplacer le "drawRect":
Nagaraj
Oui, la première réponse stackoverflow.com/a/2962882/285694 donne cette réponse. Ma question nécessitait en fait un canal alpha, j'ai donc vérifié celui-ci - stackoverflow.com/a/2962839/285694 - Techniquement, cette question concerne la définition de l'arrière-plan sur un NSView avec un canal Alpha (comme vous pouvez le faire avec NSWindow) - Mais je pense que beaucoup de gens viennent ici pour savoir comment définir la couleur (donc la première réponse est plus populaire).
BadPirate
6

Dans Swift:

override func drawRect(dirtyRect: NSRect) {

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

    super.drawRect(dirtyRect)
}
Ixx
la source
5

Utilisez NSBox, qui est une sous-classe de NSView, nous permettant de styliser facilement

Swift 3

let box = NSBox()
box.boxType = .custom
box.fillColor = NSColor.red
box.cornerRadius = 5
onmyway133
la source
NSBox n'aide pas dans le cas de NSPopover car il a également la partie triangle.
AnaghSharma
5

Sans aucun doute le moyen le plus simple, également compatible avec les éléments de jeu de couleurs:

Swift :

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

Objectif-C :

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

Constructeur d'interface :

Ajoutez un attribut défini par l'utilisateur backgroundColordans le générateur d'interface, de type NSColor.

Ely
la source
Cela repose sur un comportement non documenté et doit être évité si vous souhaitez toujours travailler dans les futures versions d'AppKit.
nschmidt le
3

J'ai testé ce qui suit et cela a fonctionné pour moi (dans Swift):

view.wantsLayer = true
view.layer?.backgroundColor = NSColor.blackColor().colorWithAlphaComponent(0.5).CGColor
Tyler Long
la source
Avez-vous mis une image au-dessus de vous après avoir fait cela? Ou avez-vous utilisé un NSView au lieu d'un NSImageView?
justColbs
3

Dans Swift 3, vous pouvez créer une extension pour le faire:

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

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

Jetez un œil à RMSkinnedView . Vous pouvez définir la couleur d'arrière-plan de NSView à partir d'Interface Builder.

Raffael
la source
1

Dans Swift, vous pouvez sous-classer NSView et faire ceci

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

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

Juste une petite classe réutilisable (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
la source
1

Il suffit de définir backgroundColorle calque (après avoir fait reculer le calque de vue).

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

Cela prend en charge la modification de l'apparence du système (activation ou désactivation du mode sombre) pendant l'exécution de l'application. Vous pouvez également définir la couleur d'arrière-plan dans Interface Builder, si vous définissez d'abord la classe de la vue sur 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
la source