I had the same problem and tried, with a sinking heart, the solutions in this post. (While I appreciate advice that one generally should keep button titles short, I'm writing a game, and I want multi-line answers to behave like buttons).
Sometimes, you don't get there from here. My ideal was an NSButton with a multi-line label, but since I can't get that without considerable hassle, I have created a PseudoButton: an NSControl subclass that behaves like a button. It has a hand cursor to indicate 'you can click here' and it gives feedback: when you click the mouse, it changes to selectedControlColor, when you release the mouse, it returns to normal. And unlike solutions that try to stack buttons and labels, there is no problem with having labels and images on top of the view: the whole of the view is the clickable area.
import Cocoa
@IBDesignable
class PseudoButton: NSControl {
@IBInspectable var backgroundColor: NSColor = NSColor.white{
didSet{
self.needsDisplay = true
}
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let path = NSBezierPath(rect: dirtyRect)
backgroundColor.setFill()
path.fill()
NSColor.black.setStroke()
path.lineWidth = 2
path.stroke()
}
override func mouseDown(with event: NSEvent) {
self.backgroundColor = NSColor.selectedControlColor
}
override func mouseUp(with event: NSEvent) {
self.backgroundColor = NSColor.clear
guard let action = action else {return}
tryToPerform(action, with: self)
//@IBAction func pseudobuttonClicked(_ sender: PseudoButton) in the ViewController class
}
override func resetCursorRects() {
addCursorRect(bounds, cursor: .pointingHand)
}
}
You use this like any other control in the storyboard: drag a Pseudobutton in, decorate it at will, and connect it to an appropriate IBAction in your viewController class.
I like this better than meddling with NSCell. (On past experience, NSCell-based hacks are more likely to break).