Найкращий спосіб змінити колір тла для 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операції.
Пітер Хосей

О, це має сенс. Я спробував NSRectFill, але прозорість не спрацювала. Я приїжджаю до Cocoa від Cocoa Touch і здивований, побачивши, що в чомусь рамка Cocoa Touch є більш повною (!). Я думав зробити розширення класу для NSView, яке дозволить вам встановити backgroundColor так, як ви можете, NSWindow або UIView, але я не думаю, що ви можете перекрити drawRect з розширенням.
BadPirate

Вибачте, що я пропустив прозорість. Так, дотик з какао полегшує багато речей. Якщо ви також робите какао, то ваша відповідь насправді краща, оскільки вона більш портативна.
mohsenr

Я не розумію, це малювання білого кольору вгорі погляду, я тільки що спробував. Не було питання про колір тла?
аневризм

@Patrick - Можливо, вам потрібно буде зателефонувати на супер 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:перервіть будь-які веб-перегляди у вашій програмі дуже креативно! (Навіть у різних вікнах ...)
rluba

1
У методі setWantsLayer:визначено: Використовуючи переглянуті шарами шари, ви ніколи не повинні взаємодіяти безпосередньо із шаром . Порядок, коли setWantsLayer:і setLayer:як викликається, є релевантним.
Стефан

Ви не повинні встановлювати шар, але все одно можете цього досягти. Дивіться ColoredView на objc.io/isissue/14-mac/appkit-for-uikit-developers
Clafou

80

Якщо ви любитель розкадровки, ось вам не потрібен жоден рядок коду .

Додайте NSBoxяк підпогляд до NSView і відрегулюйте кадр NSBox як такий, що і для NSView.

У розділі "Розгортка" або XIB змініть позицію заголовка на "Нічого", "Тип поля" на " Спеціальний" , "Тип рамки " на "Немає", а колір рамки - на все, що вам подобається.

Ось скріншот:

введіть тут опис зображення

Це результат:

введіть тут опис зображення


Використання NSBox для мене чудово працювало. Перехід до перегляду, що підтримується шаром, спричиняв проблеми з перемальовуванням, тоді як у вікні цього немає.
Генрік

4
Це відмінне і просте рішення, яке має бути в документах
UKDataGeek

Однак цей кольоровий трикутник NSPopover?
Рибена

Я шкодую лише про те, що у мене є одна пропозиція, щоб дати цю відповідь.
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. Якщо ні, то це незалежні структури, і хоча вони можуть складатися з одних і тих же членів, вони ніколи не можуть бути "мостоподібними", оскільки це термін, що посилається лише на об'єкти (структури, що мають покажчик "isa" і передаються навколо за допомогою покажчиків), але не на загальні структури.
Рафаель Швайкерт

використовуючи результати drawRect за додатковий час малювання тощо. CALayer - це краще рішення, що перевищує 10,8 і вище.
Том Андерсен

22

Я пройшов усі ці відповіді, і жодна з них, на жаль, не працювала на мене. Однак я знайшов цей надзвичайно простий спосіб, приблизно через годину пошуку:)

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

8
Зауважте, що NSViews не завжди мають шар, і в цьому випадку це нічого не робить. Дивіться відповідь Іоретопарісі.
Калле

3
Я витратив багато часу, намагаючись виконати цей код у методі viewDidLoad свого контролера (для self.myCustomView). Я зрозумів, що ви повинні використовувати метод viewWillAppear замість цього.
серфрідер

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

Хе-хе. Це буде спрацьовувати, якщо вам подобається мати випадковий колір фону кожного разу при запуску, хоча трохи складніше, ніж робити те саме з малюванням прямої (прийнята відповідь)
BadPirate

Якщо це лише питання малювання кольором BG, то для чого потрібно переосмислити "drawRect:"
Nagaraj

Так, найкраща відповідь stackoverflow.com/a/2962882/285694 дає таку відповідь. У моєму питанні насправді потрібен альфа-канал, тому я дав цьому перевірку - stackoverflow.com/a/2962839/285694 - Технічно це питання стосується встановлення фону на NSView каналом Alpha (як можна з 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"];

Інтерфейс Builder :

Додайте визначений користувачем атрибут backgroundColorу конструкторі інтерфейсів, типу NSColor.


Це спирається на незадокументовану поведінку, і цього слід уникати, якщо ви все ще хочете працювати в майбутніх версіях 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

Швидко можна підкласировать 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

Це підтримує зміну зовнішнього вигляду в усьому світі (увімкнення або вимкнення темного режиму) під час роботи програми. Ви також можете встановити колір тла в програмі Interface Builder, якщо спочатку встановити клас подання 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
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.