I've created a custom segment control (Cocoa / macOS) by subclassing NSView
(does not use any existing controls / buttons; it's an entirely custom view with a complex set of internal constraints) that has two modes:
This works just fine, and I'm able to switch between / animate the two modes at runtime. However what I want to ultimately achieve is automatic expansion / compression based on the current window size (or switch between the two when the user is resizing the window). I want this control to be reusable without the window / view controller managing the switch, and trying to avoid switching between constraints based on 'rough' estimates from inside of a superview's layout
call (which feels like a hack).
It seems NSSegmentControl
, NSButton
etc implement NSUserInterfaceCompression
which should do what I am trying to achieve, however none of the methods in that protocol get called at any time during initial layout / intrinsic content size refresh / window resize etc. I also find the documentation lacking; the only useful information I found was inside the NSSegmentControl
header files. The protocol seems to be exactly what I need - for the system to call the appropriate methods to determine a minimum / ideal size and ask the control to resize itself when space is at a premium.
For what it's worth, I've tried subclassing NSButton
too (for various reasons, I need to stick to subclassing NSView
) - however that did not trigger any of these methods either (i.e. from NSUserInterfaceCompression
).
Any idea what I'm missing?
It seems NSUserInterfaceCompression
is a dead end. For now I've reported this as feedback / bug regarding the inadequate documentation (FB9062854).
The way I ended up solving this is by:
let priorityToResistCompression = contentCompressionResistancePriority(for: .horizontal)
setContentCompressionResistancePriority(priorityToResistCompression, for: .horizontal)
The last segment (inner NSView
subviews) within the control has a trailing anchor set with priority defaultLow
to allow it to break so that the control can continue to stretch
Override setFrameSize
and determine the best mode to display (compressed, single segment as a drop down, or all the segments if they can fit horizontally). Then call invalidateIntrinsicContentSize()
for the content size to be recomputed.
Using the mode determined in the previous step, override intrinsicContentSize
and offer the correct size (the minimum compressed version or the one where all segments can fit).
This way the control wraps all of this functionality into a single NSView
subclass and relieves any superview / window hosting this control of setting the correct size as the window is resized.