21
votes

I am currently working on a custom themed NSButton. Every tutorial, or guide I have found requires to subclass NSButtonCell, even the guide from Apple.

All of those seem to be outdated, because all cell methods in NSControl are deprecated in Yosemite. I have not found any recommendations or guides what to use as a substitute.

This is the only statement, I could find:

Gradual deprecation of NSCell

Mac OS X 10.10 takes another step towards the eventual deprecation of cells. Direct access to the cell of a control is discouraged, and methods which allow it will be formally deprecated in a subsequent release. A variety of cell-level APIs have been promoted to various Control subclasses in order to provide cell-free access to important functionality. NSLevelIndicator, NSTextField, NSSearchField, NSSlider, and NSPathControl all have new properties for this purpose. Cell-based NSTableViews are now deprecated, and view-based NSTableViews should be used instead. Matrix-based NSBrowsers are also deprecated in favor of the item-based interface.

Excerpt from: AppKit Release Notes for OS X v10.10

No words on NSButton though.

NSTextField supports layer backed views; because of that, I tried the same approach on my NSButton, but that has no effect.

var btn = NSButton(NSMakeRect(0, 0, 50, 20))
btn.wantsLayer = true
btn.bordered = false
btn.layer?.backgroundColor = NSColor(calibratedWhite: 0.99, alpha: 1).CGColor
btn.layer?.borderWidth = 1
btn.layer?.borderColor = NSColor(calibratedWhite: 0.81, alpha: 1).CGColor
1
As a little side note, a cheat if you will, I have been creating custom views-- then making an invisible button on top of it. Saves a lot of headache.A O
You can simply give your theme as background image of the button.Sheen Vempeny
@MattyAyOh seems easy, but more like a hack. I don’t think this is the intended way to do it and I would like to know the intended way. But thanks for sharing this trick.Afterlame
@SheenVempeny Yes, but this introduces other problems. I can not animate specific properties, need different versions for different resolutions and so on. Especially on simple buttons I would like to generate them by code.Afterlame

1 Answers

4
votes

I would definitely spend more time looking into the layer-backed view approach. I'm not sure why it didn't work for you because there's no reason for layers not to work on an NSButton (effectively an NSView derivative).

Also weighing on looking more into layers is your mentioning of animation.

Some code extracted from a project I am working on (custom NSButton):

From init()...

    self.wantsLayer = true
    self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay

    self.layer?.borderColor = NSColor.gridColor().CGColor
    self.layer?.borderWidth = 0.5
    self.layer?.backgroundColor = NSColor.whiteColor().CGColor

You can then get fine-grained control in the display cycle specific to layers with:

override var wantsUpdateLayer:Bool{
    return true
}

override func updateLayer() {
    // your code here
    super.updateLayer()
}

If your custom button needs a shape then you can even use a CAShapeLayer below to make your backing layer a special shape...more detail to be looked into.

override func makeBackingLayer() -> CALayer {
    return CAShapeLayer()
}