1

I want to create an extension to UISlider that will allow me to use a callback to perform an action when the slider value has changed. In other words, it would have the same effect of adding an action using UIControl.Event.valueChanged.

So instead of adding an action like this:

let mySlider = UISlider()

mySlider.addTarget(self, action: #selector(sliderDidChangeValue(_:)), for: .valueChanged)

 @objc func sliderDidChangeValue(_ sender: UISlider) {
        print(sender.value)
 }

I want to create an extension like this, but one where the callback behaves the same as the action above:

extension UISlider {

convenience init(value: Float = 0,
    range: ClosedRange<Float> = 0 ... 1,
    callback: @escaping (_ x: Float) -> Void = { _ in}) {

    self.init()
    self.value = value
    self.minimumValue = range.lowerBound
    self.maximumValue = range.upperBound

   }
}



let mySlider = UISlider(value: 10, range: 1...10) { (newValue) in
            print(newValue)
}

It seems to me it should be possible to implement, but I'm really not sure how to go about it. Thanks in advance for any help.

lxirsh
  • 93
  • 8
  • Take a look at this - it is for UIButton with closure, but could easily be adjusted for UISlider https://stackoverflow.com/questions/25919472/adding-a-closure-as-target-to-a-uibutton – rbaldwin May 21 '20 at 08:29
  • Thanks for the link. I tried that solution, but unfortunately it doesn't work with multiple instances. – lxirsh May 21 '20 at 12:22

1 Answers1

1

If you want to add a closure as target to a UISlider, you have to add a function to UISlider by using extension.

@objc class SliderClass: NSObject {
    let closure: (_ val:Float)->()

    init (_ closure: @escaping (_ val:Float)->()) {
        self.closure = closure
    }

    @objc func sliderValueChange(sender:UISlider) {
        closure(sender.value)
    }
}

extension UISlider {
    func addAction(for controlEvents: UIControl.Event = .valueChanged, value : Float = 1, range : ClosedRange<Float> = 0 ... 1, _ closure: @escaping (_ val:Float)->()) {

        self.minimumValue = range.lowerBound
        self.maximumValue = range.upperBound
        self.value = value
        let classObj = SliderClass(closure)
        addTarget(classObj, action: #selector(SliderClass.sliderValueChange), for: controlEvents)
        objc_setAssociatedObject(self, "", classObj, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
    }
}

Use in your class

slider.addAction(value:1.9,range: 0 ... 2) { (newValue) in
      print(String(format: "%.2f", newValue))
 }

OR

slider.addAction(value:1.5, range: 0 ... 2) {
      print(String(format: "%.2f", $0))
}
Saifan Nadaf
  • 1,715
  • 1
  • 17
  • 28