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.

BadPirate
sumber

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

}
Mohsenr
sumber
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!

loretoparisi
sumber
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

JZAU
sumber
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]];
Gabriel
sumber
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));
}
BadPirate
sumber
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);
rumah kaca
sumber
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)
Leo Dabus
sumber
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);
    }
}
Nagaraj
sumber
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)
}
Ixx
sumber
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
onmyway133
sumber
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.

Ely
sumber
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
Tyler Long
sumber
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)
Kaiyuan Xu
sumber
1

Lihatlah RMSkinnedView . Anda dapat mengatur warna latar belakang NSView dari dalam Interface Builder.

Raffael
sumber
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;
    }
}
Chad Scira
sumber
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
Vlad
sumber
1

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

view.wantsLayer = true
view.layer?.backgroundColor = CGColor.white
Geri Borbás
sumber
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
    }
}
kareman
sumber