0

我通过子类化创建了一个自定义段控件(Cocoa / macOS)NSView(不使用任何现有的控件/按钮;它是一个具有一组复杂内部约束的完全自定义视图),具有两种模式:

  1. 默认水平显示所有段:[segment 1] [segment 2] [segment 3]
  2. 当所有段都无法适应窗口/当前约束集(受周围控件及其约束的影响)时,将单个段显示为下拉列表:[段 1]

这工作得很好,我可以在运行时在两种模式之间切换/动画。但是我最终想要实现的是基于当前窗口大小的自动扩展/压缩(或者当用户调整窗口大小时在两者之间切换)。我希望这个控件可以在没有窗口/视图控制器管理开关的情况下重复使用,并试图避免在基于超级视图调用内部的“粗略”估计的约束之间切换layout(感觉就像是黑客攻击)。

似乎NSSegmentControlNSButton实现NSUserInterfaceCompression了我想要实现的目标,但是在初始布局/内在内容大小刷新/窗口调整大小等期间,该协议中的任何方法都不会被调用。我还发现缺少文档;我发现的唯一有用的信息是在NSSegmentControl头文件中。该协议似乎正是我所需要的 - 系统调用适当的方法来确定最小/理想大小并在空间有限时要求控件调整自身大小。

对于它的价值,我也尝试过子类化NSButton(出于各种原因,我需要坚持使用子类NSView化)——但这也没有触发任何这些方法(即 from NSUserInterfaceCompression)。

知道我错过了什么吗?

4

2 回答 2

0

这似乎NSUserInterfaceCompression是一个死胡同。目前,我已将此报告为有关文档不足 (FB9062854) 的反馈/错误。

我最终解决这个问题的方法是:

  1. 在自定义控件上设置以下内容压缩:
    let priorityToResistCompression = contentCompressionResistancePriority(for: .horizontal)
    setContentCompressionResistancePriority(priorityToResistCompression, for: .horizontal)
  1. 控件中的最后一段(内部NSView子视图)有一个带有优先级的尾随锚设置,defaultLow以允许它中断,以便控件可以继续拉伸

  2. 覆盖setFrameSize并确定要显示的最佳模式(压缩、单个片段作为下拉列表,或所有片段(如果它们可以水平适合))。然后要求invalidateIntrinsicContentSize()重新计算内容大小。

  3. 使用在上一步中确定的模式,覆盖intrinsicContentSize并提供正确的大小(最小压缩版本或所有段都可以容纳的版本)。

通过这种方式,控件将所有这些功能包装到单个NSView子类中,并在调整窗口大小时减轻托管此控件的任何超级视图/窗口设置正确大小的负担。

于 2021-04-01T10:22:15.240 回答
0

好奇...稍微搜索一下,我可以找到很少的信息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...
            }
        }
        
    }

}
于 2021-03-31T20:34:58.903 回答