NSSlider 自定义子类 - 如何保持旋钮位置和用户交互之间的 link?

NSSlider custom subclass - how to maintain the link between the knob position and user interaction?

正在尝试创建自定义 NSSlider。覆盖 NSSliderCelldrawKnob() 方法会改变旋钮的外观,但这样做会以某种方式断开旋钮位置和用户与滑块交互之间的 link。

在经常引用的 objective C 示例 (https://github.com/lucasderraugh/LADSlider) 中,当您覆盖 drawKnob() 时,您似乎需要显式处理 startTracking 方法,但我还没有找到适合我的解决方案 - 目前我只是将单元格的 startTracking 'at' 属性 设置为滑块的当前值,但不确定正确的方法是什么。

相当多的示例在单元格的自定义初始化程序中包含一个 NSCoder 参数 - 我不明白为什么,但这可能与确保旋钮显示和实际滑块值之间的连接有关?

import Cocoa

class ViewController: NSViewController {

    var seekSlider = NSSlider()
    var seekSliderCell = SeekSliderCell()

    override func viewDidLoad() {
        
        super.viewDidLoad()
        seekSlider = NSSlider(target: self, action: #selector(self.seek(_:)))
        seekSlider.cell = seekSliderCell
        seekSlider.cell?.target = self
        seekSlider.cell?.action = #selector(self.seek(_:))
        seekSlider.isEnabled = true
        seekSlider.isContinuous = true
        view.addSubview(seekSlider)
    }
    
    @objc func seek(_ sender: NSObject) {
        
        let val = seekSlider.cell?.floatValue
        let point = NSPoint(x: Double(val!), y: 0.0)
        seekSlider.cell?.startTracking(at: point, in: self.seekSlider)
    }
}

class SeekSliderCell: NSSliderCell {
    
//  required init(coder aDecoder: NSCoder) {
//      super.init(coder: aDecoder)
//  }
    
    override func drawKnob() {
        
        let frame = NSRect(x: 0.0, y: 6.0, width: 20.0, height: 10.0)
        let c = NSColor(red: 0.9, green: 0.0, blue: 0.6, alpha: 1.0)
        c.setFill()
        NSBezierPath.init(roundedRect: frame, xRadius: 3, yRadius: 3).fill()
    }

    override func startTracking(at startPoint: NSPoint, in controlView: NSView) -> Bool {

       return true
    }
}


drawKnob() 的文档指出:

Special Considerations If you create a subclass of NSSliderCell, don’t override this method. Override drawKnob(_:) instead.

而不是

func drawKnob()

覆盖

func drawKnob(_ knobRect: NSRect)

示例:

class ViewController: NSViewController {

    var seekSlider = NSSlider()
    var seekSliderCell = SeekSliderCell()

    override func viewDidLoad() {
        super.viewDidLoad()
        seekSlider = NSSlider(target: self, action: #selector(self.seek(_:)))
        seekSlider.cell = seekSliderCell
        seekSlider.cell?.target = self
        seekSlider.cell?.action = #selector(self.seek(_:))
        seekSlider.isEnabled = true
        seekSlider.isContinuous = true
        view.addSubview(seekSlider)
    }

    @objc func seek(_ sender: NSObject) {
        let val = seekSlider.cell?.floatValue
        print("\(String(describing: val))")
    }

}

class SeekSliderCell: NSSliderCell {

    override func drawKnob(_ knobRect: NSRect) {
        var frame = NSRect(x: 0.0, y: 6.0, width: 20.0, height: 10.0)
        frame.origin.x = knobRect.origin.x + (knobRect.size.width - frame.size.width) / 2
        frame.origin.y = knobRect.origin.y + (knobRect.size.height - frame.size.height) / 2
        let c = NSColor(red: 0.9, green: 0.0, blue: 0.6, alpha: 1.0)
        c.setFill()
        NSBezierPath.init(roundedRect: frame, xRadius: 3, yRadius: 3).fill()
    }

}