Cara terbaik untuk mengubah warna latar belakang untuk NSView


149

Saya mencari cara terbaik untuk mengubah backgroundColorsuatu NSView. Saya juga ingin dapat mengatur alphatopeng yang sesuai untuk NSView. Sesuatu seperti:

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

Saya perhatikan bahwa NSWindowmemiliki metode ini, dan saya bukan penggemar besar NSColorWheel, atau NSImageopsi latar belakang, tetapi jika mereka yang terbaik, bersedia untuk digunakan.

Jawaban:


134

Ya, jawaban Anda sendiri benar. Anda juga bisa menggunakan metode Kakao:

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

Dalam 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
NSRectFilldigunakan NSCompositeCopyuntuk mengisi, sehingga akan menghancurkan apa pun di belakangnya — itu tidak akan bergabung di atas pandangan leluhur mana pun. Untuk mengisi dengan warna transparan sebagian (atau sepenuhnya), gunakan NSRectFillUsingOperation developer.apple.com/mac/library/documentation/Cocoa/Reference/… dengan NSCompositeSourceOveroperasi.
Peter Hosey

Oh, itu masuk akal. Saya mencoba NSRectFill tetapi transparansi tidak berhasil. Saya datang ke Cocoa Touch dari Cocoa Touch, dan terkejut melihat bahwa dalam beberapa hal kerangka kerja Cocoa Touch lebih lengkap (!). Saya sedang berpikir untuk membuat ekstensi kelas untuk NSView yang akan memungkinkan Anda untuk mengatur backgroundColor seperti Anda dapat NSWindow atau UIView, tapi saya tidak berpikir Anda dapat mengganti drawRect dengan ekstensi.
BadPirate

Maaf saya melewatkan bagian transparansi. Ya, sentuhan Kakao membuat banyak hal lebih mudah. Jika Anda juga melakukan Cocoa touch, jawaban Anda sendiri sebenarnya lebih baik, karena lebih portabel.
mohsenr

Saya tidak mengerti, ini menggambar warna putih di atas tampilan, saya baru saja mencoba. Bukankah pertanyaan tentang warna latar belakang?
aneuryzm

@ Patrick - Anda mungkin perlu memanggil super drawRect :) atau jika Anda memiliki beberapa hal lain yang seharusnya digambar di atas latar belakang, pastikan bahwa itu setelah panggilan NSRectFill.
BadPirate

116

Solusi mudah dan efisien adalah mengonfigurasi tampilan untuk menggunakan lapisan Core Animation sebagai backing store-nya. Kemudian Anda bisa menggunakan -[CALayer setBackgroundColor:]untuk mengatur warna latar belakang layer.

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

Itu dia!


6
Hati-hati: menelepon setLayer:atau setWantsLayer:menghancurkan WebViews apa pun yang mungkin Anda miliki di aplikasi dengan cara yang sangat kreatif! (Bahkan di jendela yang berbeda ...)
rluba

1
Dalam metode setWantsLayer:ini didefinisikan: Saat menggunakan tampilan yang didukung lapisan Anda tidak boleh berinteraksi langsung dengan lapisan . Urutan kapan setWantsLayer:dan setLayer:dipanggil relevan.
Stephan

Anda tidak harus mengatur layer tetapi masih bisa mencapai ini. Lihat ColoredView di objc.io/issues/14-mac/appkit-for-uikit-developers
Clafou

80

Jika Anda seorang pencinta storyboard, berikut adalah cara Anda tidak perlu baris kode apa pun .

Menambahkan NSBox sebagai subview ke NSView dan sesuaikan frame NSBox dengan NSView.

Di Storyboard atau XIB ubah posisi Judul menjadi Tidak Ada, ketik Kotak menjadi Kustom , Jenis Perbatasan ke "Tidak Ada", dan warna Perbatasan ke apa pun yang Anda suka.

Ini screenshotnya:

masukkan deskripsi gambar di sini

Ini hasilnya:

masukkan deskripsi gambar di sini


Menggunakan NSBox bekerja sangat baik untuk saya. Beralih ke tampilan yang didukung lapisan menyebabkan masalah redraw, sedangkan kotak tidak.
Henrik

4
Ini adalah solusi luar biasa dan sederhana yang harus ada dalam dokumen
UKDataGeek

Apakah warna NSPopover ini segitiga?
Ribena

Saya hanya menyesal bahwa saya memiliki satu upvote untuk memberikan jawaban ini.
orion elenzil

33

Jika Anda mengaturWantsLayer ke YA terlebih dahulu, Anda dapat secara langsung memanipulasi lapisan latar belakang.

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

26

Pikir saya tahu bagaimana cara melakukannya:

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

Terima kasih. Saya perlu mengubah dirtyRect ke CGRect. Ubah baris terakhir menjadi CGContextFillRect(context, NSRectToCGRect(dirtyRect));Bisakah Anda mengedit jawaban Anda?
Ross

1
Mereka dulunya adalah jembatan bebas pulsa: /
BadPirate

6
Saat membangun untuk 64-bit atau iOS atau membangun 32-bit seperti 64-bit, NSRect hanyalah cara lain untuk mengatakan CGRect. Namun, jika tidak, ini adalah struct independen, dan meskipun mereka mungkin terdiri dari anggota yang sama, mereka tidak akan pernah bisa "bebas pulsa" karena itu adalah istilah yang merujuk hanya ke objek (struct yang memiliki pointer "isa" dan diedarkan menggunakan pointer), tetapi tidak untuk struktur generik.
Raphael Schweikert

menggunakan drawRect menghasilkan tambahan waktu draw, dll. CALayer adalah solusi yang lebih baik di 10.8 ke atas.
Tom Andersen

22

Saya memeriksa semua jawaban ini dan sayangnya tidak ada yang bekerja untuk saya. Namun, saya menemukan cara yang sangat sederhana ini, setelah sekitar satu jam mencari:)

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

8
Perhatikan bahwa NSViews tidak selalu memiliki layer, dalam hal ini tidak melakukan apa-apa. Lihat jawaban Ioretoparisi.
Kalle

3
Saya menghabiskan banyak waktu untuk mencoba melakukan kode ini dalam metode viewDidLoad pada pengontrol saya (untuk self.myCustomView). Saya tahu bahwa Anda harus menggunakan metode viewWillAppear sebagai gantinya.
peselancar

21

edit / perbarui: 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
        }
    }
}

pemakaian:

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)

Saya pikir ini adalah solusi terbersih, tetapi saya prihatin dengan pernyataan yang dibuat oleh orang-orang Objc.io di sini: objc.io/issues/14-mac/appkit-for-uikit-developers - "... Anda tidak boleh mencoba berinteraksi dengan lapisan secara langsung, karena AppKit memiliki lapisan itu. " Saya tidak yakin apa yang salah. Either way, saya mengadopsi solusi ini dalam kode saya sendiri.
Kevin Sliech

7

Solusi terbaik :

- (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. Itu akan bekerja jika Anda ingin memiliki warna latar belakang acak setiap kali Anda meluncurkan, meskipun sedikit lebih rumit kemudian melakukan hal yang sama dengan menggambar persegi (jawaban yang diterima)
BadPirate

Jika itu hanya masalah menggambar warna BG maka apa yang perlu dilakukan untuk mengganti "drawRect:"
Nagaraj

Ya, jawaban teratas stackoverflow.com/a/2962882/285694 memberikan respons itu. Pertanyaan saya sebenarnya harus memiliki saluran alfa, jadi saya memberi ini cek - stackoverflow.com/a/2962839/285694 - Secara teknis pertanyaan ini adalah tentang mengatur latar belakang pada NSView dengan saluran Alpha (seperti yang Anda bisa dengan NSWindow) - Tapi saya pikir banyak orang datang ke sini mencari cara mengatur warna (dengan demikian jawaban pertama lebih populer).
BadPirate

6

Dalam Swift:

override func drawRect(dirtyRect: NSRect) {

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

    super.drawRect(dirtyRect)
}

5

Gunakan NSBox, yang merupakan subkelas dari NSView, memungkinkan kita untuk dengan mudah bergaya

Cepat 3

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

NSBox tidak membantu dalam kasus NSPopover karena NSP juga memiliki bagian segitiga.
AnaghSharma

5

Tanpa ragu cara termudah, juga kompatibel dengan Color Set Assets:

Cepat :

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

Tujuan-C :

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

Pembuat Antarmuka :

Tambahkan atribut yang ditentukan pengguna backgroundColordi pembangun antarmuka, jenis NSColor.


Ini bergantung pada perilaku tidak berdokumen dan harus dihindari jika Anda masih ingin bekerja di versi AppKit yang akan datang.
nschmidt

3

Saya menguji yang berikut dan itu berhasil untuk saya (di Swift):

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

Apakah Anda meletakkan gambar di atas tampilan Anda setelah Anda melakukan ini? Atau apakah Anda menggunakan NSView alih-alih NSImageView?
justColbs

3

Di Swift 3, Anda dapat membuat ekstensi untuk melakukannya:

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

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


1

Dengan cepat Anda dapat mensubklasifikasikan NSView dan melakukan ini

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

Kelas reusable yang kecil (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

Cukup atur backgroundColorpada layer (setelah membuat tampilan layer didukung).

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

0

Ini mendukung perubahan tampilan seluruh sistem (menghidupkan atau mematikan mode gelap) saat aplikasi sedang berjalan. Anda juga dapat mengatur warna latar belakang di Interface Builder, jika Anda mengatur kelas tampilan ke BackgroundColorView terlebih dahulu.


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
    }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.