如何为 UISlider 实现 shouldChangeValue(to: Int)?

How to implement shouldChangeValue(to: Int) for UISlider?

我只有 UISlider 和 minValue 0 和 maxValue 100。我的 currentValue 是 40.

有没有办法检查@IBAction

中的currentValue
@objc private func didValueChanged(slider: UISlider) {

    // check if it is not over 200, 
    // if not then set previous value. 
    // How do I get previous value of slider?
}

没有超过我的限制并且do nothing then否则将以前的值设置为滑块?

不清楚当滑块的总和超过 200 个限制时你想要做什么,但总体思路...

您需要跟踪每个滑块的最后有效 值。当用户停止拖动时:

  • 获取“活动”滑块的值
  • 获取其他 5 个滑块的总和
  • 将“有效”值添加到总和
  • 如果总和为out-of-range(总和 > 200)
    • 重置为活动滑块的保存值
  • 其他
    • 更新活动滑块的保存值

所以,给你的滑块这个动作:

slider.addTarget(self, action: #selector(didStopSliding(slider:)), for: [.touchUpInside, .touchUpOutside, .touchCancel])

当用户停止滑动时调用(抬起触摸或滑动 off-screen 或取消操作)。

您可以保留当前的:

slider.addTarget(self, action: #selector(didValueChanged(slider:)), for: .valueChanged)

如果您想在拖动时做一些事情(例如更新值标签)。

这是一个简单的例子:

// struct to associate values and display labels with the UISlider
struct MySliderStruct {
    var minValue: Float = 0
    var maxValue: Float = 0
    var curValue: Float = 0
    var minLabel: UILabel = UILabel()
    var curLabel: UILabel = UILabel()
    var maxLabel: UILabel = UILabel()
    var theSlider: UISlider = UISlider()
}

class SliderCheckVC: UIViewController {
    
    var theSliders: [MySliderStruct] = []
    
    let runningTotalLabel: UILabel = {
        let v = UILabel()
        v.textAlignment = .center
        return v
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let minMax: [[Float]] = [
            [10, 40],
            [20, 50],
            [0, 10],
            [0, 50],
            [10, 60],
            [0, 100],
        ]
        let colors: [UIColor] = [
            .init(red: 1.0, green: 0.8, blue: 0.8, alpha: 1.0),
            .init(red: 0.5, green: 1.0, blue: 0.5, alpha: 1.0),
            .init(red: 0.8, green: 0.8, blue: 1.0, alpha: 1.0),
            .init(red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0),
            .init(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0),
            .init(red: 0.0, green: 1.0, blue: 1.0, alpha: 1.0),
        ]
        
        let outerStack: UIStackView = {
            let v = UIStackView()
            v.axis = .vertical
            v.spacing = 16
            v.translatesAutoresizingMaskIntoConstraints = false
            return v
        }()
        
        for (pair, clr) in zip(minMax, colors) {

            // vertical stack view for labels + slider
            let vStack: UIStackView = {
                let v = UIStackView()
                v.axis = .vertical
                v.spacing = 4
                return v
            }()
            // horizontal stack view for the labels
            let hStack: UIStackView = {
                let v = UIStackView()
                v.distribution = .fillEqually
                return v
            }()
            let vMin: UILabel = {
                let v = UILabel()
                v.textAlignment = .left
                v.font = .systemFont(ofSize: 13.0)
                v.text = formatVal(pair[0])
                return v
            }()
            let vCur: UILabel = {
                let v = UILabel()
                v.textAlignment = .center
                v.font = .systemFont(ofSize: 13.0)
                v.text = formatVal(pair[0])
                return v
            }()
            let vMax: UILabel = {
                let v = UILabel()
                v.textAlignment = .right
                v.font = .systemFont(ofSize: 13.0)
                v.text = formatVal(pair[1])
                return v
            }()
            hStack.addArrangedSubview(vMin)
            hStack.addArrangedSubview(vCur)
            hStack.addArrangedSubview(vMax)
            
            let slider = UISlider()
            slider.minimumValue = pair[0]
            slider.maximumValue = pair[1]
            slider.setValue(pair[0], animated: false)
            slider.addTarget(self, action: #selector(didValueChanged(slider:)), for: .valueChanged)
            slider.addTarget(self, action: #selector(didStopSliding(slider:)), for: [.touchUpInside, .touchUpOutside, .touchCancel])

            vStack.backgroundColor = clr

            vStack.addArrangedSubview(hStack)
            vStack.addArrangedSubview(slider)
            outerStack.addArrangedSubview(vStack)

            let thisSlider: MySliderStruct = MySliderStruct(minValue: pair[0],
                                                            maxValue: pair[1],
                                                            curValue: pair[0],
                                                            minLabel: vMin,
                                                            curLabel: vCur,
                                                            maxLabel: vMax,
                                                            theSlider: slider
            )
            theSliders.append(thisSlider)
        }

        // running total line
        let hStack: UIStackView = {
            let v = UIStackView()
            v.distribution = .fillEqually
            return v
        }()
        let v1 = UILabel()
        v1.text = "Current Total"
        v1.textAlignment = .center
        runningTotalLabel.textAlignment = .center
        hStack.addArrangedSubview(v1)
        hStack.addArrangedSubview(runningTotalLabel)
        outerStack.addArrangedSubview(hStack)
        
        view.addSubview(outerStack)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            outerStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 12.0),
            outerStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            outerStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
        ])

        updateTotalLabel()
    }

    @objc private func didStopSliding(slider: UISlider) {

        guard let thisSliderIDX = theSliders.firstIndex(where: { [=12=].theSlider == slider})
        else {
            print("Invalide slider setup")
            return
        }

        // get the value of "active" slider
        let thisValue: Float = slider.value.rounded()
        
        // get sum of all other sliders, plus
        //  this slider value
        var total: Float = 0
        for i in 0..<theSliders.count {
            if i == thisSliderIDX {
                total += thisValue
            } else {
                total += theSliders[i].curValue
            }
        }

        // if sum is less-than max allowed
        if total < 200.0 {
            // update array with new value
            theSliders[thisSliderIDX].curValue = thisValue
            // move slider to rounded Int value
            slider.setValue(thisValue, animated: true)
        } else {
            // reset to last value
            slider.setValue(theSliders[thisSliderIDX].curValue, animated: true)
            // update this slider's current value label
            theSliders[thisSliderIDX].curLabel.text = formatVal(theSliders[thisSliderIDX].curValue)
        }

        updateTotalLabel()
        
    }
    @objc private func didValueChanged(slider: UISlider) {
        // do something as the slider is dragged
        guard let thisSliderIDX = theSliders.firstIndex(where: { [=12=].theSlider == slider})
        else {
            print("Invalide slider setup")
            return
        }

        // get the value of "active" slider
        let thisValue: Float = slider.value.rounded()

        // update this slider's current value label
        theSliders[thisSliderIDX].curLabel.text = formatVal(thisValue)

        // get sum of all other sliders, plus
        //  this slider value
        var total: Float = 0
        for i in 0..<theSliders.count {
            if i == thisSliderIDX {
                total += thisValue
            } else {
                total += theSliders[i].curValue
            }
        }
        
        // update the running total label here, because
        //  we might throw out this value
        runningTotalLabel.text = formatVal(total)
    }
    
    func updateTotalLabel() {
        let sumOfValues = theSliders.reduce(0) { [=12=] + (.curValue ) }
        runningTotalLabel.text = formatVal(sumOfValues)
    }
    func formatVal(_ val: Float) -> String {
        return "\(Int(val))"
    }
}

看起来像这样:

在“完成滑动”时,如果将滑块拖动到一个值,导致总和超过 200,则该滑块将重置为其上一个“有效”值。