วิธีที่ดีที่สุดในการเปลี่ยนสีพื้นหลังสำหรับ NSView


149

ฉันกำลังมองหาวิธีที่ดีที่สุดในการเปลี่ยนแปลงของbackgroundColor NSViewที่ฉันยังชอบที่จะสามารถตั้งค่าที่เหมาะสมหน้ากากสำหรับalpha NSViewสิ่งที่ต้องการ:

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

ฉันสังเกตเห็นว่าNSWindowมีวิธีนี้และฉันไม่ใช่แฟนตัวยงของNSColorWheelหรือNSImageตัวเลือกพื้นหลัง แต่ถ้าพวกเขาดีที่สุดเต็มใจที่จะใช้

คำตอบ:


134

ใช่คำตอบของคุณเองถูกต้อง คุณสามารถใช้วิธีโกโก้:

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

ในสวิฟต์:

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

โอ้นั่นทำให้รู้สึก ฉันลอง NSRectFill แต่ความโปร่งใสไม่ได้ผล ฉันมาที่ Cocoa จาก Cocoa Touch และแปลกใจที่เห็นว่ากรอบ Cocoa Touch นั้นสมบูรณ์กว่า (!) ฉันกำลังคิดที่จะสร้างส่วนขยายของชั้นเรียนสำหรับ NSView ที่จะช่วยให้คุณตั้งค่า backgroundColor เช่นเดียวกับ NSWindow หรือ UIView แต่ฉันไม่คิดว่าคุณจะสามารถแทนที่ drawRect ด้วยส่วนขยายได้
— BadPirate

ขอโทษที่ฉันพลาดส่วนความโปร่งใส ใช่สัมผัสโกโก้ทำให้หลายสิ่งง่ายขึ้น หากคุณกำลังทำโกโก้ด้วยเช่นกันคำตอบของคุณจะดีกว่าเพราะมันพกพาได้มากกว่า
— mohsenr

ฉันไม่เข้าใจนี่คือการวาดสีขาวด้านบนของมุมมองฉันเพิ่งลอง คำถามเกี่ยวกับสีพื้นหลังไม่ใช่หรือ
— aneuryzm

@Patrick - คุณอาจต้องเรียก super drawRect :) หรือถ้าคุณมีสิ่งอื่นที่ควรจะวาดที่ด้านบนของพื้นหลังตรวจสอบให้แน่ใจว่าพวกเขาอยู่หลังจากการโทร NSRectFill
— BadPirate

116

วิธีแก้ปัญหาที่ง่ายและมีประสิทธิภาพคือการกำหนดค่ามุมมองให้ใช้เลเยอร์ภาพเคลื่อนไหวหลักเป็นที่เก็บข้อมูลสำรอง จากนั้นคุณสามารถใช้-[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:แบ่งมุมมอง WebV ใด ๆ ที่คุณอาจมีในใบสมัครของคุณด้วยวิธีที่สร้างสรรค์มาก! (แม้แต่ในหน้าต่างที่แตกต่างกัน ... )
— rluba

1
setWantsLayer:มีการกำหนดใน methode : เมื่อใช้มุมมองเลเยอร์ที่ได้รับการสนับสนุนคุณไม่ควรโต้ตอบกับเลเยอร์โดยตรง คำสั่งของเวลาsetWantsLayer:และที่setLayer:เรียกว่ามีความเกี่ยวข้อง
— เตฟาน

คุณไม่ควรตั้งค่าเลเยอร์ แต่ยังสามารถทำสิ่งนี้ได้ ดู ColoredView ที่objc.io/issues/14-mac/appkit-for-uikit-developers
— Clafou

80

หากคุณเป็นคนรักสตอรี่บอร์ดนี่คือวิธีการที่คุณ ไม่จำเป็นต้องบรรทัดของรหัสใด ๆ

เพิ่ม NSBoxเป็นมุมมองย่อยไปที่ NSView และปรับกรอบของ NSBox เหมือนกันกับ NSView

ในกระดานเรื่องราวหรือ XIB เปลี่ยนตำแหน่งของชื่อเป็นไม่มี, ชนิดของกล่องเป็นแบบกำหนดเอง , ชนิดของเส้นขอบเป็น "ไม่มี" และสีของเส้นขอบเป็นสิ่งที่คุณต้องการ

นี่คือภาพหน้าจอ:

ป้อนคำอธิบายรูปภาพที่นี่

นี่คือผลลัพธ์ที่ได้:

ป้อนคำอธิบายรูปภาพที่นี่


การใช้ NSBox นั้นยอดเยี่ยมมากสำหรับฉัน การสลับไปใช้มุมมองแบบเลเยอร์ที่ได้รับการสนับสนุนทำให้เกิดปัญหาในการวาดใหม่
— Henrik

4
นี่เป็นทางออกที่ยอดเยี่ยมและเรียบง่ายที่ควรอยู่ในเอกสาร
— UKDataGeek

สีนี้ NSPopover เป็นสามเหลี่ยมไหม
— Ribena

ฉันเสียใจเพียงว่าฉันมี แต่หนึ่ง upvote ที่จะให้คำตอบนี้
— 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

6
เมื่อสร้าง 64 บิตหรือ iOS หรือสร้าง 32- บิตเช่น 64- บิต NSRect เป็นอีกวิธีหนึ่งในการพูด CGRect ถ้าไม่อย่างไรก็ตามสิ่งเหล่านี้เป็นโครงสร้างที่เป็นอิสระและแม้ว่าพวกเขาอาจประกอบด้วยสมาชิกคนเดียวกันพวกเขาไม่สามารถ "โทรฟรี - สะพาน -" เนื่องจากเป็นคำที่อ้างถึงวัตถุเท่านั้น (structs ที่มีตัวชี้ "isa" และถูกส่งผ่านโดยใช้พอยน์เตอร์) แต่ไม่ใช่โครงสร้างทั่วไป
— Raphael Schweikert

การใช้ผลลัพธ์ drawRect ในเวลาวาดที่เพิ่มเป็นต้น CALayer เป็นโซลูชันที่ดีขึ้นตั้งแต่ 10.8 ขึ้นไป
— Tom Andersen

22

ฉันผ่านทุกคำตอบเหล่านี้และไม่มีใครทำงานให้ฉันได้อย่างน่าเสียดาย อย่างไรก็ตามฉันพบวิธีที่ง่ายที่สุดนี้หลังจากผ่านการค้นหาประมาณหนึ่งชั่วโมง:)

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

8
โปรดทราบว่า NSViews ไม่ได้มีเลเยอร์เสมอไปซึ่งในกรณีนี้จะไม่ทำอะไรเลย ดูคำตอบของ Ioretoparisi
— Kalle

3
ฉันใช้เวลามากในการพยายามทำโค้ดนี้ในวิธี viewDidLoad ของคอนโทรลเลอร์ (สำหรับ self.myCustomView) ฉันคิดว่าคุณควรใช้วิธี viewWillAppear แทน
— surfrider

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 เป็นเจ้าของเลเยอร์เหล่านั้น ". ฉันไม่แน่ใจว่าจะเกิดอะไรขึ้น ทั้งสองวิธีฉันใช้วิธีนี้ในรหัสของตัวเอง
— Kevin Sliech

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

ฮิฮิ. มันจะใช้ได้ถ้าคุณต้องการให้มีสีพื้นหลังแบบสุ่มทุกครั้งที่คุณเปิดตัวแม้ว่าจะซับซ้อนกว่าเล็กน้อยจากนั้นก็ทำแบบเดียวกันกับการวาดรูปสี่เหลี่ยม (คำตอบที่ยอมรับ)
— BadPirate

หากเป็นเพียงเรื่องของการวาดสี BG แล้วสิ่งที่จำเป็นในการเอาชนะ "drawRect:"
— Nagaraj

ใช่คำตอบสูงสุดstackoverflow.com/a/2962882/285694ให้คำตอบนั้น คำถามของฉันจำเป็นต้องมีช่องอัลฟาดังนั้นฉันจึงให้เช็ค - stackoverflow.com/a/2962839/285694 - ในทางเทคนิคคำถามนี้เกี่ยวกับการตั้งค่าพื้นหลังบน NSView ด้วยช่องอัลฟ่า (เช่นเดียวกับ NSWindow) - แต่ฉันคิดว่าผู้คนมากมายมาที่นี่เพื่อค้นหาวิธีการตั้งค่าสี (คำตอบแรกจึงเป็นที่นิยมมากขึ้น)
— BadPirate

6

ในสวิฟต์:

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

ไม่ต้องสงสัยเลยว่าวิธีที่ง่ายที่สุดเข้ากันได้กับสินทรัพย์ชุดสี:

รวดเร็ว :

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

วัตถุประสงค์ -C :

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

เครื่องมือสร้างส่วนต่อประสาน :

เพิ่มผู้ใช้แอตทริบิวต์ที่กำหนดไว้ในการสร้างอินเตอร์เฟซของประเภทbackgroundColorNSColor


สิ่งนี้ขึ้นอยู่กับพฤติกรรมที่ไม่มีเอกสารและควรหลีกเลี่ยงหากคุณยังต้องการทำงานใน AppKit เวอร์ชันอนาคต
— nschmidt

3

ฉันทดสอบต่อไปนี้และใช้งานได้สำหรับฉัน (ใน Swift):

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

คุณวางรูปภาพไว้ด้านบนของมุมมองหลังจากทำสิ่งนี้หรือไม่? หรือว่าคุณใช้ NSView แทน NSImageView
— justColbs

3

ใน Swift 3 คุณสามารถสร้างส่วนขยายเพื่อทำ:

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

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

1

มีลักษณะที่RMSkinnedView คุณสามารถตั้งค่าสีพื้นหลังของ NSView ได้จากภายในเครื่องมือสร้างส่วนต่อประสาน


1

ใน swift คุณสามารถ subclass 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

1

เพียงตั้งค่าbackgroundColorบนเลเยอร์ (หลังจากทำการสำรองข้อมูลเลเยอร์มุมมอง)

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

0

สิ่งนี้รองรับการเปลี่ยนลักษณะที่ปรากฏของระบบ (การเปิดหรือปิดโหมดมืด) ในขณะที่แอปพลิเคชันทำงานอยู่ คุณยังสามารถตั้งค่าสีพื้นหลังในตัวสร้างส่วนต่อประสานได้หากคุณตั้งค่าคลาสของมุมมองเป็น 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
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.