How to enable “tap and slide” in a UISlider?

后端 未结 14 534
不知归路
不知归路 2020-12-03 20:54

What I want to get is a UISlider which lets the user not only slide when he starts on its thumbRect, but also when he taps elsewhere. When the user

相关标签:
14条回答
  • 2020-12-03 21:38

    You should add a tap gesture on your UISlider.

    Exemple :

     UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(sliderTapped:)];
        [_slider addGestureRecognizer:tapGestureRecognizer];
    

    In sliderTapped you can get the location and update the value of the slider :

    - (void)sliderTapped:(UIGestureRecognizer *)gestureRecognizer {
        CGPoint  pointTaped = [gestureRecognizer locationInView:gestureRecognizer.view];
        CGPoint positionOfSlider = _slider.frame.origin;
        float widthOfSlider = _slider.frame.size.width;
        float newValue = ((pointTaped.x - positionOfSlider.x) * _slider.maximumValue) / widthOfSlider;
        [_slider setValue:newValue];
    }
    

    I create an example here : https://github.com/ali59a/tap-and-slide-in-a-UISlider

    0 讨论(0)
  • 2020-12-03 21:38

    Updated tsji10dra's answer to Swift 4:

    @IBAction func sliderTappedAction(sender: UITapGestureRecognizer) {
    
        if let slider = sender.view as? UISlider {
            if slider.isHighlighted { return }
    
            let point = sender.location(in: slider)
            let percentage = Float(point.x / slider.bounds.size.width)
            let delta = percentage * (slider.maximumValue - slider.minimumValue)
            let value = slider.minimumValue + delta
            slider.setValue(value, animated: true)
    
            // also remember to call valueChanged if there's any
            // custom behaviour going on there and pass the slider
            // variable as the parameter, as indicated below
            self.sliderValueChanged(slider)
        }
    }
    
    0 讨论(0)
  • 2020-12-03 21:39

    My solution is quite simple:

    class CustomSlider: UISlider {
        override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
            let newValue = <calculated_value>
            self.setValue(newValue, animated: false)
            super.sendActions(for: UIControlEvents.valueChanged)
            return true
    }}
    
    0 讨论(0)
  • 2020-12-03 21:41

    I'm not sure if you are still looking for an answer for this, but I was just looking at this myself today; and I managed to get it to work for me.

    The key to it, is using a UILongPressGestureRecognizer instead of just a UITapGestureRecognizer, we can then set the minimumPressDuration of the recognizer to 0; making it act as a tap recognizer, except you can now actually check its state.

    Putting what ali59a suggested will work for you, just by replacing the UITapGestureRecognizer with a UILongPressGestureRecognizer. However, I found that this didn't seem to quite put the thumbRect directly under my thumb. It appeared a bit off to me.

    I created my own UISlider subclass for my project, and here is how I implemented the "tap and slide feature" for me.

    In my init method:

    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(tapAndSlide:)];
    longPress.minimumPressDuration = 0;
    [self addGestureRecognizer:longPress];
    

    Then my tapAndSlide: method:

    - (void)tapAndSlide:(UILongPressGestureRecognizer*)gesture
    {
        CGPoint pt = [gesture locationInView: self];
        CGFloat thumbWidth = [self thumbRect].size.width;
        CGFloat value;
    
        if(pt.x <= [self thumbRect].size.width/2.0)
            value = self.minimumValue;
        else if(pt.x >= self.bounds.size.width - thumbWidth/2.0)
            value = self.maximumValue;
        else {
            CGFloat percentage = (pt.x - thumbWidth/2.0)/(self.bounds.size.width - thumbWidth);
            CGFloat delta = percentage * (self.maximumValue - self.minimumValue);
            value = self.minimumValue + delta;
        }
    
        if(gesture.state == UIGestureRecognizerStateBegan){
            [UIView animateWithDuration:0.35 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
                [self setValue:value animated:YES];
                [super sendActionsForControlEvents:UIControlEventValueChanged];
            } completion:nil];
        }
        else [self setValue:value];
    
        if(gesture.state == UIGestureRecognizerStateChanged)
            [super sendActionsForControlEvents:UIControlEventValueChanged];
    }
    

    Where I also used a method to return the frame of my custom thumbRect:

    - (CGRect)thumbRect {
        CGRect trackRect = [self trackRectForBounds:self.bounds];
        return [self thumbRectForBounds:self.bounds trackRect:trackRect value:self.value];
    }
    

    I also have my slider animate to the position where the user first taps, over 0.35 seconds. Which I reckon looks pretty sweet, so I included that in that code. If you don't want that, simply try this:

    - (void)tapAndSlide:(UILongPressGestureRecognizer*)gesture
    {
        CGPoint pt = [gesture locationInView: self];
        CGFloat thumbWidth = [self thumbRect].size.width;
        CGFloat value;
    
        if(pt.x <= [self thumbRect].size.width/2.0)
            value = self.minimumValue;
        else if(pt.x >= self.bounds.size.width - thumbWidth/2.0)
            value = self.maximumValue;
        else {
            CGFloat percentage = (pt.x - thumbWidth/2.0)/(self.bounds.size.width - thumbWidth);
            CGFloat delta = percentage * (self.maximumValue - self.minimumValue);
            value = self.minimumValue + delta;
        }
    
        [self setValue:value];
    
        if(gesture.state == UIGestureRecognizerStateChanged)
            [super sendActionsForControlEvents:UIControlEventValueChanged];
    }
    

    I hope that makes sense, and helps you.

    0 讨论(0)
  • 2020-12-03 21:42

    Here is my solution that works :

    import UIKit
    
    class CustomSlider: UISlider {
        
        override init(frame: CGRect) {
            super.init(frame: frame)
            setupView()
        }
        
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            setupView()
        }
        
        
        private func setupView() {
            addTapGesture()
        }
        
        private 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)
        }
    }
    
    0 讨论(0)
  • 2020-12-03 21:44

    Adding swift version of Ali AB.'s answer,

    @IBAction func sliderTappedAction(sender: UITapGestureRecognizer)
    {
        if let slider = sender.view as? UISlider {
    
            if slider.highlighted { return }
    
            let point = sender.locationInView(slider)
            let percentage = Float(point.x / CGRectGetWidth(slider.bounds))
            let delta = percentage * (slider.maximumValue - slider.minimumValue)
            let value = slider.minimumValue + delta
            slider.setValue(value, animated: true)
        }
    }
    
    0 讨论(0)
提交回复
热议问题