Лучший способ изменить цвет фона для 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

Извините, я пропустил часть прозрачности. Да, прикосновение какао облегчает многие вещи. Если вы также делаете Какао, то ваш собственный ответ на самом деле лучше, так как он более переносим.
Мохсенр

Я не понимаю, это рисует белый цвет поверх вида, я только что попробовал. Не был вопрос о цвете фона?
aneuryzm

@Patrick - Вам может потребоваться вызвать super 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:ломая любые WebViews, которые вы можете иметь в своем приложении, очень креативно! (Даже в разных окнах ...)
rluba

1
В методе setWantsLayer:определено: При использовании представлений со слоем вы никогда не должны напрямую взаимодействовать со слоем . Порядок, когда setWantsLayer:и setLayer:называется, является актуальным.
Стефан

Вы не должны устанавливать слой, но все еще можете достичь этого. См. Цветное изображение на objc.io/issues/14-mac/appkit-for-uikit-developers
Клафу

80

Если вы любитель раскадровки, то вам не понадобится ни одной строки кода .

Добавьте NSBoxв качестве подпредставления NSView и настройте кадр NSBox так же, как NSView.

В раскадровке или XIB измените положение заголовка на «Нет», тип поля - « Пользовательский» , «тип границы» - «нет», а цвет границы - на любой другой.

Вот скриншот:

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

Это результат:

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


Использование NSBox отлично сработало для меня. Переключение на представление с поддержкой слоев вызывало проблемы с перерисовкой, а окно - нет.
Хенрик

4
Это отличное и простое решение, которое должно быть в документах
UKDataGeek

Есть ли у этого цвета треугольники NSPopover?
Рибена

я сожалею только о том, что у меня есть только одно возражение, чтобы дать этот ответ.
orion elenzil

33

Если вы вначале установили для WW значение «Да», вы можете напрямую управлять фоном слоя.

[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
Раньше они были бесплатными
мостами

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 вместо этого.
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 владеет этими слоями. " Я не уверен, что может пойти не так. В любом случае, я принимаю это решение в своем собственном коде.
Кевин Слиех

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:»
Нагарадж

Да, верхний ответ 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, что позволяет нам легко стилизовать

Swift 3

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

NSBox не помогает в случае NSPopover, поскольку он также имеет треугольную часть.
AnaghSharma

5

Без сомнения, самый простой способ, также совместимый с Color Set Assets:

Свифт :

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

Цель-C :

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

Интерфейсный Разработчик :

Добавить пользовательский атрибут backgroundColorв построителе интерфейса, типа NSColor.


Это зависит от недокументированного поведения, и его следует избегать, если вы все еще хотите работать в будущих версиях AppKit.
июня

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

В Swift вы можете создать подкласс 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
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.