I've been playing around with NSTouchBar
in a macOS application and came up with the following code:
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
switch identifier {
case NSTouchBarItemIdentifier.testItemIdentifier:
let viewObject = NSView()
for i in 1...10 {
let imageWidth: CGFloat = 20;
let imageHeight: CGFloat = 20;
let imagePositionTop: CGFloat = 0
let imagePositionLeft: CGFloat = imageWidth * (CGFloat(i) - 1)
let imageObject = NSImage(named: "test_image")!
imageObject.size = NSSize(width: 20, height: 20)
let imageView = NSImageView(image: imageObject)
let constraintObject1 = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: viewObject, attribute: NSLayoutAttribute.top, multiplier: 1, constant: imagePositionTop)
let constraintObject2 = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.left, relatedBy: NSLayoutRelation.equal, toItem: viewObject, attribute: NSLayoutAttribute.left, multiplier: 1, constant: imagePositionLeft)
let constraintObject3 = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: imageView, attribute: NSLayoutAttribute.width, multiplier: 0, constant: imageWidth)
let constraintObject4 = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: imageView, attribute: NSLayoutAttribute.height, multiplier: 0, constant: imageHeight)
imageView.addConstraint(constraintObject1)
imageView.addConstraint(constraintObject2)
imageView.addConstraint(constraintObject3)
imageView.addConstraint(constraintObject4)
viewObject.addSubview(imageView)
}
let customViewItem = NSCustomTouchBarItem(identifier: identifier)
customViewItem.view = viewObject
return customViewItem
default:
return nil
}
}
When running the application, I get an EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
. The cause of this seems to be the calls to addConstraint
. I've never created constraints manually before, and am relatively new to the swift language.
Any help is greatly appreciated.
@toddg is correct: you must add imageView
as a subview of viewObject
before you can activate a constraint between the two views. Two views must be in the same view hierarchy if you want to activate a constraint involving both of them.
But you have other problems.
A constraint between two views A and B must be added to a common ancestor of A and B (which may be A or B itself). In your case, since imageView
is a subview of viewObject
, that means that any constraint between imageView
and viewObject
must be added to viewObject
(or a superview of viewObject
). You cannot add the constraint to imageView
. You can fix this by setting the isActive
property of the constraint to true instead of using addConstraint
. When you set the isActive
property to true, the constraint will add itself to the appropriate view.
Also, you're creating a constraint between imageView.width
and imageView.width
, which doesn't make sense. If you want to constrain imageView.width
to a constant, the other item of the constraint should be nil
with an attribute of .notAnAttribute
.
Also, you're not setting translatesAutoresizingMaskIntoConstraints
to false, which you need to do if you want to use constraints to set the size and position of the imageView
.
Anyway, you're using a bunch of old API that makes things harder than necessary. You can make your code correct, and much shorter and simpler, by using an NSStackView
. NSStackView
was added in macOS 10.9, and NSTouchBar
was added in macOS 10.12.2, so there's no reason not to use a stack view for this.
func touchBar(_ touchBar: NSTouchBar, makeItemForIdentifier identifier: NSTouchBarItemIdentifier) -> NSTouchBarItem? {
switch identifier {
case NSTouchBarItemIdentifier.testItemIdentifier:
let stack = NSStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.spacing = 0
stack.distribution = .fillEqually
let length = CGFloat(20)
for _ in 1...10 {
let imageObject = NSImage(named: "test_image")!
imageObject.size = NSSize(width: length, height: length)
let imageView = NSImageView(image: imageObject)
imageView.translatesAutoresizingMaskIntoConstraints = false
stack.addArrangedSubview(imageView)
}
let customViewItem = NSCustomTouchBarItem(identifier: identifier)
customViewItem.view = stack
return customViewItem
default:
return nil
}
}
Result: