我通过子类化NSView
(不使用任何现有控件/按钮;它是一个完全自定义的视图,具有一组复杂的内部约束(创建了一个自定义段控件(Cocoa/macOS(,它有两种模式:
- 默认情况下,水平显示所有分段:[sece 1][sece 2][sece 3]
- 当所有线段都无法适应窗口/当前约束集(受周围控件及其约束的影响(时,将单个线段显示为下拉列表:[线段1🔽 ]
这很好,我可以在运行时在两种模式之间切换/设置动画。然而,我最终想要实现的是基于当前窗口大小的自动扩展/压缩(或者在用户调整窗口大小时在两者之间切换(。我希望这个控件可以重复使用,而不需要窗口/视图控制器来管理切换,并试图避免在基于超级视图的layout
调用内部的"粗略"估计的约束之间切换(这感觉像是黑客攻击(。
NSSegmentControl
、NSButton
等似乎实现了NSUserInterfaceCompression
,这应该是我试图实现的,然而,在初始布局/内部内容大小刷新/窗口大小调整等过程中,该协议中的任何方法都没有被调用。我也发现文档缺乏;我发现的唯一有用的信息是在CCD_ 6头文件中。该协议似乎正是我所需要的——系统调用适当的方法来确定最小/理想大小,并要求控件在空间紧张时自行调整大小。
值得一提的是,我也尝试过对NSButton
进行子类化(出于各种原因,我需要坚持对NSView
进行子类划分(,但这也没有触发任何这些方法(即从NSUserInterfaceCompression
开始(。
知道我错过了什么吗?
好奇。。。稍微搜索一下,我就可以找到关于NSUserInterfaceCompression
的非常少的信息?
不确定你需要做什么,但这种方法可能对你有用:
class SegTestView: NSView {
let segCtrl = NSSegmentedControl()
var curWidth: CGFloat = 0
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
addSubview(segCtrl)
segCtrl.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
segCtrl.topAnchor.constraint(equalTo: topAnchor),
segCtrl.leadingAnchor.constraint(equalTo: leadingAnchor),
segCtrl.trailingAnchor.constraint(equalTo: trailingAnchor),
segCtrl.bottomAnchor.constraint(equalTo: bottomAnchor),
])
}
override func layout() {
super.layout()
// only execute if bounds.width has changed
if curWidth != bounds.width {
curWidth = bounds.width
segCtrl.segmentCount = 3
segCtrl.setLabel("First", forSegment: 0)
segCtrl.setLabel("Second", forSegment: 1)
segCtrl.setLabel("Third", forSegment: 2)
if segCtrl.intrinsicContentSize.width > bounds.size.width {
segCtrl.segmentCount = 1
segCtrl.setLabel("Single 🔽", forSegment: 0)
} else {
// in case you want to do something else here...
}
}
}
}
似乎NSUserInterfaceCompression
是一条死胡同。目前,我已经将此报告为关于文档不足的反馈/错误(FB9062854(。
我最终解决这个问题的方法是:
- 在自定义控件上设置以下内容压缩:
let priorityToResistCompression = contentCompressionResistancePriority(for: .horizontal)
setContentCompressionResistancePriority(priorityToResistCompression, for: .horizontal)
控件内的最后一段(内部
NSView
子视图(有一个优先级为defaultLow
的拖尾锚,允许其断裂,以便控件可以继续拉伸覆盖
setFrameSize
,并确定要显示的最佳模式(压缩的、单个分段作为下拉菜单,或者所有分段(如果它们可以水平放置((。然后调用invalidateIntrinsicContentSize()
以重新计算内容大小。使用上一步中确定的模式,覆盖
intrinsicContentSize
并提供正确的大小(最小压缩版本或所有段都可以容纳的版本(。
通过这种方式,控件将所有这些功能封装到一个单独的NSView
子类中,并在调整窗口大小时免除托管此控件的任何超级视图/窗口设置正确大小的责任。