Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tap on UISlider to Set the Value

I created a Slider (operating as control of the video, like YouTube has at the bottom) and set the maximum (duration) and minimum values. And then used SeekToTime to change the currentTime. Now, the user can slide the thumb of the slider to change the value

What I want to achieve is letting the user tap on anywhere on the slider and set the current time of the video.

I got an approach from this answer, and I tried to apply it to my case, but couldn't make it work

class ViewController: UIViewController, PlayerDelegate {

var slider: UISlider!

override func viewDidLoad() {
  super.viewDidLoad()

  // Setup the slider
}

func sliderTapped(gestureRecognizer: UIGestureRecognizer) {
  //  print("A")

  let pointTapped: CGPoint = gestureRecognizer.locationInView(self.view)

  let positionOfSlider: CGPoint = slider.frame.origin
  let widthOfSlider: CGFloat = slider.frame.size.width
  let newValue = ((pointTapped.x - positionOfSlider.x) * CGFloat(slider.maximumValue) / widthOfSlider)

  slider.setValue(Float(newValue), animated: true)      
 }
}

I thought this should work, but no luck. Then I tried debugging with trying to printing "A" to logs, but it doesn't so apparently it doesn't go into the sliderTapped() function.

What am I doing wrong? Or is there a better way to achieve what I am trying to achieve?

like image 765
senty Avatar asked Jan 05 '16 19:01

senty


4 Answers

Looks like you need to actually initialize the tap gesture recognizer in your viewDidLoad() per the code example above. There's a comment there, but I don't see the recognizer being created anywhere.

Swift 2:

class ViewController: UIViewController {

    var slider: UISlider!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Setup the slider

        // Add a gesture recognizer to the slider
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "sliderTapped:")
        self.slider.addGestureRecognizer(tapGestureRecognizer)
    }

    func sliderTapped(gestureRecognizer: UIGestureRecognizer) {
        //  print("A")

        let pointTapped: CGPoint = gestureRecognizer.locationInView(self.view)

        let positionOfSlider: CGPoint = slider.frame.origin
        let widthOfSlider: CGFloat = slider.frame.size.width
        let newValue = ((pointTapped.x - positionOfSlider.x) * CGFloat(slider.maximumValue) / widthOfSlider)

        slider.setValue(Float(newValue), animated: true)      
    }
}

Swift 3:

class ViewController: UIViewController {

    var slider: UISlider!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Setup the slider

        // Add a gesture recognizer to the slider
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(sliderTapped(gestureRecognizer:)))
        self.slider.addGestureRecognizer(tapGestureRecognizer)
    }

    func sliderTapped(gestureRecognizer: UIGestureRecognizer) {
        //  print("A")

        let pointTapped: CGPoint = gestureRecognizer.location(in: self.view)

        let positionOfSlider: CGPoint = slider.frame.origin
        let widthOfSlider: CGFloat = slider.frame.size.width
        let newValue = ((pointTapped.x - positionOfSlider.x) * CGFloat(slider.maximumValue) / widthOfSlider)

        slider.setValue(Float(newValue), animated: true)
    }
}
like image 146
myuiviews Avatar answered Oct 08 '22 19:10

myuiviews


It seems like just subclassing UISlider and returning always true to the beginTracking produce the desired effect.

iOS 10 and Swift 3

class CustomSlider: UISlider {
    override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        return true
    }
}

Use the CustomSlider instead of UISlider afterwards in your code.

like image 33
pteofil Avatar answered Oct 08 '22 21:10

pteofil


I like this approach

extension UISlider {
    public func addTapGesture() {
        let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        addGestureRecognizer(tap)
    }

    @objc private func handleTap(_ sender: UITapGestureRecognizer) {
        let location = sender.location(in: self)
        let percent = minimumValue + Float(location.x / bounds.width) * maximumValue
        setValue(percent, animated: true)
        sendActions(for: .valueChanged)
    }
}

And later just call

let slider = UISlider()
slider.addTapGesture()
like image 4
Adam Avatar answered Oct 08 '22 21:10

Adam


This is my code, based on "myuiviews" answer.
I fixed 2 little "bugs" of the original code.

1 - Tapping on 0 was too difficult, so I made it easier
2 - Sliding the slider's thumb just a little bit was also firing the "tapGestureRecognizer", which makes it return to the initial position, so I added a minimum distance filter to avoid that.

Swift 4

class ViewController: UIViewController {

    var slider: UISlider!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Setup the slider

        // Add a gesture recognizer to the slider
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(sliderTapped(gestureRecognizer:)))
        self.slider.addGestureRecognizer(tapGestureRecognizer)
    }

    @objc func sliderTapped(gestureRecognizer: UIGestureRecognizer) {
        //  print("A")

        var pointTapped: CGPoint = gestureRecognizer.location(in: self.view)
        pointTapped.x -= 30  //Subtract left constraint (distance of the slider's origin from "self.view" 

        let positionOfSlider: CGPoint = slider.frame.origin
        let widthOfSlider: CGFloat = slider.frame.size.width

        //If tap is too near from the slider thumb, cancel
        let thumbPosition = CGFloat((slider.value / slider.maximumValue)) * widthOfSlider
        let dif = abs(pointTapped.x - thumbPosition)
        let minDistance: CGFloat = 51.0  //You can calibrate this value, but I think this is the maximum distance that tap is recognized
        if dif < minDistance { 
            print("tap too near")
            return
        }

        var newValue: CGFloat
        if pointTapped.x < 10 {
            newValue = 0  //Easier to set slider to 0
        } else {
            newValue = ((pointTapped.x - positionOfSlider.x) * CGFloat(slider.maximumValue) / widthOfSlider)
        }



        slider.setValue(Float(newValue), animated: true)
    }
}
like image 1
Diogo Souza Avatar answered Oct 08 '22 21:10

Diogo Souza