更改NSView背景颜色的最佳方法


149

我在寻找改变的最佳途径backgroundColorNSView。我还希望能够为设置适当的alpha蒙版NSView。就像是:

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

我注意到NSWindow有这种方法,我也不喜欢NSColorWheelNSImage背景选项,但如果它们是最好的,那就愿意使用。

Answers:


134

是的,您自己的答案是正确的。您还可以使用可可方法:

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

在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
NSRectFill用于NSCompositeCopy填充,因此它会掩盖其后的所有内容-不会在任何祖先视图的顶部合成。对于具有部分(或完全)透明颜色的填充,请在操作中使用NSRectFillUsingOperation developer.apple.com/mac/library/documentation/Cocoa/Reference/…NSCompositeSourceOver
Peter Hosey 2010年

哦,那很有道理。我尝试了NSRectFill,但是透明度不起作用。我从Cocoa Touch来到Cocoa,惊讶地发现Cocoa Touch框架在某些方面更加完善(!)。我当时在考虑为NSView创建类扩展,该类扩展允许您像设置NSWindow或UIView一样设置backgroundColor,但是我认为您不能使用扩展覆盖drawRect。
BadPirate

抱歉,我错过了透明部分。是的,可可触感使许多事情变得容易。如果您也同时使用可可粉,那么您的答案实际上会更好,因为它更便于携带。
mohsenr 2010年

我不明白,这是我在尝试在视图上方绘制白色的方法。不是关于背景色的问题吗?
aneuryzm

@Patrick-您可能需要调用super drawRect :),或者如果应该在背景顶部绘制其他一些东西,请确保它们在NSRectFill调用之后。
BadPirate

116

一个简单,有效的解决方案是将视图配置为使用Core Animation层作为其后备存储。然后,您可以-[CALayer setBackgroundColor:]用来设置图层的背景色。

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

而已!


6
小心:以非常有创意的方式调用setLayer:setWantsLayer:中断应用程序中可能包含的任何WebView!(即使在不同的窗口中……)
rluba 2012年

1
在methode setWantsLayer:中定义:在使用层支持的视图时,永远不要直接与层交互。when setWantsLayer:setLayer:被调用的顺序是相关的。
斯蒂芬

您不应该设置图层,但是仍然可以实现这一点。参见objc.io/issues/14-mac/appkit-for-uikit-developers上的ColoredView
Clafou 2015年

80

如果您是情节提要的爱好者,那么可以通过这种方式不需要任何代码行

添加NSBox为NSView的子视图,并调整NSBox的框架,使其与NSView相同。

在情节提要或XIB中,将“标题”位置更改为“无”,将“ 框类型”更改为“ 自定义 ”,将“边框类型” 更改为“无”,并将“边框颜色”更改为任何您喜欢的颜色。

这是屏幕截图:

在此处输入图片说明

结果如下:

在此处输入图片说明


使用NSBox对我来说很棒。切换到支持图层的视图会导致重绘问题,而框却没有。
亨里克

4
这是一个出色且简单的解决方案,应该放在文档中
UKDataGeek

这是否会为NSPopover三角形上色?
Ribena

我仅感到遗憾的是,我只有一个投票赞成这个答案。
Orion elenzil

33

如果首先将WantsLayer设置为YES,则可以直接操纵图层背景。

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

26

想想我想出了怎么做:

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

谢谢。我需要将dirtyRect转换为CGRect。在“ CGContextFillRect(context, NSRectToCGRect(dirtyRect));可以编辑答案吗?
罗斯,

1
他们曾经是免费电话桥接的:/
BadPirate 2011年

6
当为64位或iOS构建或像32位一样构建32位时,NSRect只是表达CGRect的另一种方式。但是,如果不是,则它们是独立的结构,即使它们可能由相同的成员组成,也绝不能“免费桥接”,因为该术语仅指对象(具有“ isa”指针的结构)并使用指针传递),但不传递给通用结构。
拉斐尔·施维克

使用drawRect会增加绘制时间,等等。在10.8及更高版本中,CALayer是更好的解决方案。
汤姆·安德森

22

我经历了所有这些答案,不幸的是没有一个对我有用。但是,经过大约一个小时的搜索,我发现了这种极其简单的方法:)

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

8
请注意,NSViews并不总是具有图层,在这种情况下,它什么也不做。请参阅Ioretoparisi的答案。
Kalle 2013年

3
我花了很多时间试图在控制器的viewDidLoad方法中执行此代码(对于self.myCustomView)。我发现您应该改用viewWillAppear方法。
surfrider 2015年

21

编辑/更新: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
        }
    }
}

用法:

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)

我认为这是最干净的解决方案,但我对Objc.io员工在此处所做的声明感到担忧:objc.io/issues/14-mac/appkit-for-uikit-developers- “ ...您不应尝试进行交互直接与图层一起使用,因为AppKit拥有这些图层。”。我不确定会出什么问题。无论哪种方式,我都会在自己的代码中采用此解决方案。
凯文·斯利希

7

最佳解决方案:

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

呵呵。如果您希望每次启动时都具有随机的背景颜色,那会起作用,尽管稍微复杂一点然后使用draw rect(接受的答案)进行相同操作
BadPirate 2013年

如果仅是绘制BG颜色的问题,那么覆盖“ drawRect:”的必要条件是什么?
Nagaraj 2013年

是的,最重要的答案stackoverflow.com/a/2962882/285694给出了响应。我的问题实际上需要具有alpha通道,所以我给了它一个检查-stackoverflow.com/a/2962839/285694-从技术上讲,这个问题是关于在具有Alpha通道的NSView上设置背景(就像您可以使用NSWindow一样) -但是我认为很多人来这里只是在寻找如何设置颜色的方法(因此第一个答案更受欢迎)。
BadPirate

6

在Swift中:

override func drawRect(dirtyRect: NSRect) {

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

    super.drawRect(dirtyRect)
}

5

使用NSBox,它是NSView的子类,使我们可以轻松地设置样式

迅捷3

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

NSBox在具有NSPopover的情况下也没有帮助,因为它也具有三角形部分。
AnaghSharma

5

毫无疑问,最简单的方法也与Color Set Assets兼容:

斯威夫特

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

目标-C

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

界面生成器

backgroundColor在类型为的界面构建器中添加用户定义的属性NSColor


这依赖于未记录的行为,如果仍要在AppKit的将来版本中使用,则应避免这种情况。
nschmidt

3

我测试了以下内容,并且对我有用(在Swift中):

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

完成此操作后,您是否在视图上方放置了图片?还是您使用NSView而不是NSImageView?
justColbs 2015年

3

在Swift 3中,您可以创建一个扩展来做到这一点:

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

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


1

您可以迅速将NSView子类化并执行此操作

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

很小的可重用类(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


0

这支持在应用程序运行时更改系统范围的外观(打开或关闭暗模式)。如果先将视图的类设置为BackgroundColorView,则还可以在Interface Builder中设置背景色。


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
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.