Search code examples
iphonerubyuigesturerecognizeruisliderrubymotion

How to detect that a UISlider has been tapped using RubyMotion?


I have UISlider:

slider = UISlider.alloc.initWithFrame([[20, 70)], [260, 40]])

I want to be able to detect when the user clicks on it and know where. I figure that I'd need to use UITapGestureRecognizer or something similar, but I am very new and clueless to all this.

I can get to find out when the slider is tapped with this:

slider.addTarget(self, action:'sliderTaped', forControlEvents:UIControlEventTouchDown)

... but it only works on the thumb, so that's not very useful, I'd rather catch any tap on the slider.

Any pointers or links to documentation would be appreciated.


Related question/answer, but with objective-c: Clickable UISlider


Solution

  • To get notified when the slider has been changed, you need to set up the target/action. The target is the object containing the method you want called, and the action is the method you want called. I called my action sliderValueChanged:. You can choose anything. Be sure to append the colon to the name because that tells it to send a pointer to the sender which is the UISlider object. You need the sender so that you can ask for the value of the slider.

    If you don't set a minimumValue and maximumValue for the slider, the values will range from 0.0 to 1.0.

    def viewDidLoad
        slider = UISlider.alloc.initWithFrame([[20, 70], [260, 40]])
        slider.addTarget(self, action:'sliderValueChanged:', forControlEvents:UIControlEventValueChanged)
        self.view.addSubview(slider)
    end
    
    def sliderValueChanged(sender)
        puts sender.value
    end
    

    As shown, sliderValueChanged will get called continuously as the user moves the slider. To be notified only when the user stops touching the slider, set:

    slider.continuous = no
    

    Now, if you want a clickable slider that moves the thumb to where you click, then you need to add a UITapGestureRecognizer to the slider. This code demonstrates this:

    def viewDidLoad
        slider = UISlider.alloc.initWithFrame([[50, 50], [200, 40]])
        tapGestureRecognizer = UITapGestureRecognizer.alloc.initWithTarget(self, action:'sliderTapped:')
        tapGestureRecognizer.numberOfTapsRequired = 2
        slider.addGestureRecognizer(tapGestureRecognizer)
    
        slider.continuous = false
        slider.minimumValue = 77
        slider.maximumValue = 129
        slider.addTarget(self, action:'sliderValueChanged:', forControlEvents:UIControlEventValueChanged)
        self.view.addSubview(slider)
    end
    
    # This is called when the slider changes either from taps or from moving the thumb
    def handle_new_slider_value(value)
        puts "New slider value: #{value}"
    end
    
    def sliderValueChanged(slider)
        handle_new_slider_value(slider.value)
    end
    
    # These were determined empirically by setting the thumb to the minimum and
    # then clicking on the center and printing out gestureRecognizer.locationInView(slider).x
    # and then repeating for the maximum.
    SLIDER_MIN = 11
    SLIDER_MAX = 189
    
    def sliderTapped(gestureRecognizer)
        if gestureRecognizer.state == UIGestureRecognizerStateEnded
            slider = gestureRecognizer.view
            x = gestureRecognizer.locationInView(slider).x
            # Uncomment this to determine values for SLIDER_MIN/SLIDER_MAX
            # puts x
            x = SLIDER_MIN if x < SLIDER_MIN
            x = SLIDER_MAX if x > SLIDER_MAX
            slider_min_val = slider.minimumValue
            slider_max_val = slider.maximumValue
    
            # Convert from the location in the view to the slider value
            slider.value = slider_min_val + (x - SLIDER_MIN) / (SLIDER_MAX - SLIDER_MIN) * (slider_max_val - slider_min_val)
            handle_new_slider_value(slider.value)
        end
    end
    

    Note that this code moves the slider on a double tap. I had trouble with just a single tap because if you move the thumb quickly, the code treats it as a tap and snaps the thumb back to its starting position. Set tapGestureRecognizer.numberOfTapsRequired = 1 to play around with the single tap.