Cách tốt nhất để thay đổi màu nền cho NSView


149

Tôi đang tìm cách tốt nhất để thay đổi backgroundColormột NSView. Tôi cũng muốn có thể đặt alphamặt nạ thích hợp cho NSView. Cái gì đó như:

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

Tôi nhận thấy rằng NSWindowcó phương pháp này và tôi không phải là một fan hâm mộ lớn của các tùy chọn NSColorWheelhoặc NSImagenền, nhưng nếu chúng là tốt nhất, sẵn sàng sử dụng.

Câu trả lời:


134

Vâng, câu trả lời của riêng bạn là đúng. Bạn cũng có thể sử dụng các phương pháp ca cao:

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

Trong 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
NSRectFillsử dụng NSCompositeCopyđể thực hiện điền, do đó, nó sẽ ghi đè lên bất cứ thứ gì đằng sau nó. Nó sẽ không kết hợp trên bất kỳ chế độ xem tổ tiên nào. Để tô màu với màu trong suốt một phần- (hoặc hoàn toàn-), hãy sử dụng NSRectFillUsingOperation developer.apple.com/mac/l Library / documentation / Coloa / Reference / Lỗi với NSCompositeSourceOverthao tác.
Peter Hosey

Ồ điều đó cũng có lý. Tôi đã thử NSRectFill nhưng tính minh bạch không hoạt động. Tôi đến với Cốc Cốc từ Ca cao cảm ứng, và ngạc nhiên khi thấy rằng trong một số cách, khung công tác Cacao Touch hoàn thiện hơn (!). Tôi đã nghĩ đến việc tạo một tiện ích mở rộng lớp cho NSView cho phép bạn thiết lập nềnColor giống như bạn có thể là một NSWindow hoặc UIView, nhưng tôi không nghĩ bạn có thể ghi đè drawRect bằng một tiện ích mở rộng.
BadPirate

Xin lỗi tôi đã bỏ lỡ phần minh bạch. Yeah, ca cao cảm ứng làm cho nhiều thứ dễ dàng hơn. Nếu bạn cũng đang thực hiện ca cao, câu trả lời của bạn thực sự tốt hơn, vì nó dễ mang theo hơn.
mohsenr

Tôi không hiểu, đây là vẽ màu trắng trên đầu của khung nhìn, tôi vừa thử. Không phải là câu hỏi về màu nền?
aneuryzm

@Patrick - Bạn có thể cần gọi super drawRect :) hoặc nếu bạn có một số thứ khác được cho là được vẽ trên nền, hãy đảm bảo rằng chúng nằm sau lệnh gọi NSRectFill.
BadPirate

116

Một giải pháp dễ dàng, hiệu quả là cấu hình chế độ xem để sử dụng lớp Core Animation làm kho lưu trữ dự phòng. Sau đó, bạn có thể sử dụng -[CALayer setBackgroundColor:]để đặt màu nền của lớp.

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

Đó là nó!


6
Cẩn thận: gọi setLayer:hoặc setWantsLayer:phá vỡ bất kỳ WebView nào bạn có thể có trong ứng dụng của mình theo những cách rất sáng tạo! (Ngay cả trong các cửa sổ khác nhau ...)
rluba

1
Trong methode setWantsLayer:được xác định: Khi sử dụng các khung nhìn được hỗ trợ lớp, bạn không bao giờ nên tương tác trực tiếp với lớp . Thứ tự khi setWantsLayer:setLayer:được gọi là có liên quan.
Stephan

Bạn không nên đặt lớp nhưng vẫn có thể đạt được điều này. Xem ColourView tại objc.io/issues/14-mac/appkit-for-uikit-developers
Clafou

80

Nếu bạn là người yêu thích kịch bản, đây là một cách mà bạn không cần bất kỳ dòng mã nào .

Thêm NSBoxdưới dạng một khung nhìn phụ vào NSView và điều chỉnh khung của NSBox giống như với NSView.

Trong Storyboard hoặc XIB, thay đổi vị trí Tiêu đề thành Không có, Loại hộp thành Tùy chỉnh , Loại viền thành "Không" và Màu viền thành bất cứ thứ gì bạn thích.

Đây là một ảnh chụp màn hình:

nhập mô tả hình ảnh ở đây

Đây là kết quả:

nhập mô tả hình ảnh ở đây


Sử dụng NSBox làm việc rất tốt cho tôi. Chuyển sang chế độ xem được hỗ trợ lớp đã gây ra sự cố vẽ lại, trong khi hộp thì không.
Henrik

4
Đây là một giải pháp tuyệt vời và đơn giản mà phải ở trong các tài liệu
UKDataGeek

Có màu NSPopover này mặc dù?
Ribena

tôi chỉ tiếc rằng tôi có nhưng một upvote để đưa ra câu trả lời này.
orion elenzil

33

Nếu bạn đặtWantsLayer thành CÓ trước, bạn có thể thao tác trực tiếp với lớp nền.

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

26

Nghĩ rằng tôi đã tìm ra cách để làm điều đó:

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

Cảm ơn. Tôi cần chuyển đổi DirtRect thành CGRect. Sửa đổi dòng cuối cùng để CGContextFillRect(context, NSRectToCGRect(dirtyRect));bạn có thể chỉnh sửa câu trả lời của bạn?
Ross

1
Họ từng được miễn phí bắc cầu: /
BadPirate

6
Khi xây dựng cho 64 bit hoặc iOS hoặc xây dựng 32 bit như 64 bit, NSRect chỉ là một cách khác để nói CGRect. Tuy nhiên, nếu không, đây là những cấu trúc độc lập và mặc dù chúng có thể bao gồm cùng một thành viên, chúng không bao giờ có thể là cầu nối miễn phí của Wap vì đó là một thuật ngữ chỉ các đối tượng (các cấu trúc có con trỏ isa is và được truyền xung quanh bằng cách sử dụng các con trỏ), nhưng không cho các cấu trúc chung.
Raphael Schweikert

sử dụng kết quả drawRect trong thời gian rút thăm thêm, v.v ... CALayer là giải pháp tốt hơn trong 10.8 trở lên.
Tom Andersen

22

Tôi đã xem qua tất cả những câu trả lời này và không ai trong số chúng làm việc cho tôi một cách đáng tiếc. Tuy nhiên, tôi tìm thấy cách cực kỳ đơn giản này, sau khoảng một giờ tìm kiếm :)

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

8
Lưu ý rằng NSView không phải lúc nào cũng có một lớp, trong trường hợp này, không có gì. Xem câu trả lời của Ioretoparisi.
Kalle

3
Tôi đã dành nhiều thời gian để thử thực hiện mã này trong phương thức viewDidLoad của bộ điều khiển của tôi (đối với self.myCustomView). Tôi đã tìm ra rằng bạn nên sử dụng phương pháp viewWillAppear thay thế.
surfrider

21

chỉnh sửa / cập nhật: 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
        }
    }
}

sử dụng:

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)

Tôi nghĩ rằng đây là giải pháp sạch nhất, nhưng lo ngại về tuyên bố mà những người Objc.io đưa ra ở đây: objc.io/issues/14-mac/appkit-for-uikit-developers - "... bạn không nên cố gắng tương tác với các lớp trực tiếp, vì AppKit sở hữu các lớp đó. ". Tôi không chắc những gì có thể đi sai. Dù bằng cách nào, tôi đang áp dụng giải pháp này trong mã của riêng tôi.
Kevin Sliech

7

Giải pháp tốt nhất :

- (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. Điều đó sẽ hoạt động nếu bạn muốn có một màu nền ngẫu nhiên mỗi khi bạn khởi chạy, mặc dù phức tạp hơn một chút sau đó thực hiện tương tự với vẽ trực tiếp (câu trả lời được chấp nhận)
BadPirate

Nếu đó chỉ là vấn đề vẽ màu BG thì điều cần thiết là ghi đè "drawRect:"
Nagaraj

Vâng, câu trả lời hàng đầu stackoverflow.com/a/2962882/285694 đưa ra câu trả lời đó. Câu hỏi của tôi thực sự bắt buộc phải có kênh alpha, vì vậy tôi đã đưa ra câu hỏi này - stackoverflow.com/a/2962839/285694 - Về mặt kỹ thuật câu hỏi này là về việc đặt nền trên NSView bằng kênh Alpha (giống như bạn có thể làm với NSWindow) - Nhưng tôi nghĩ rằng rất nhiều người đến đây chỉ tìm cách thiết lập màu sắc (do đó câu trả lời đầu tiên là phổ biến hơn).
BadPirate

6

Trong Swift:

override func drawRect(dirtyRect: NSRect) {

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

    super.drawRect(dirtyRect)
}

5

Sử dụng NSBox, một lớp con của NSView, cho phép chúng tôi dễ dàng tạo kiểu

Swift 3

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

NSBox không giúp đỡ trong trường hợp NSPopover vì nó cũng có phần tam giác.
AnaghSharma

5

Không nghi ngờ gì là cách dễ nhất, cũng tương thích với Tài sản tập màu:

Swift :

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

Mục tiêu-C :

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

Trình tạo giao diện :

Thêm một thuộc tính do người dùng định nghĩa backgroundColortrong trình xây dựng giao diện, loại NSColor.


Điều này phụ thuộc vào hành vi không có giấy tờ và nên tránh nếu bạn vẫn muốn làm việc trong các phiên bản AppKit trong tương lai.
nschmidt

3

Tôi đã thử nghiệm những điều sau đây và nó hoạt động với tôi (trong Swift):

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

Bạn đã đặt một hình ảnh trên đầu bạn xem sau khi bạn làm điều này? Hay bạn đã sử dụng NSView thay vì NSImageView?
justColbs

3

Trong Swift 3, bạn có thể tạo tiện ích mở rộng để thực hiện:

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

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

1

Có một cái nhìn tại RMSkinnedView . Bạn có thể đặt màu nền của NSView từ bên trong Trình tạo giao diện.


1

Trong swift, bạn có thể phân lớp NSView và làm điều này

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

Chỉ là lớp tái sử dụng nhỏ (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

Chỉ cần đặt backgroundColortrên lớp (sau khi thực hiện lớp xem lại).

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

0

Điều này hỗ trợ thay đổi giao diện toàn hệ thống (bật hoặc tắt chế độ tối) trong khi ứng dụng đang chạy. Bạn cũng có thể đặt màu nền trong Trình tạo giao diện, nếu bạn đặt lớp của chế độ xem thành BackgroundColorView trước.


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
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.