How to create a custom themed NSButton without subclassing NSButtonCell?

cocoa, macos, swift

Solution

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

Problem

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 ```

Original source