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

后端 未结 14 533
不知归路
不知归路 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:44

    I didn't check David Williames answer, but I'll post my solution in case someone is looking for another way to do it.

    Swift 4

    First create a custom UISlider so that it will detect touches on the bar as well :

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

    (don't forget to set your slider to be this CustomSlider, on storyboard)

    The on viewDidLoad of the view controller that is displaying the slider:

    self.slider.addTarget(self, action: #selector(sliderTap), for: .touchDown)
    

    (this is only used to pause the player when moving the slider)

    Then, on your UISlider action:

    @IBAction func moveSlider(_ sender: CustomSlider, forEvent event: UIEvent) {
        if let touchEvent = event.allTouches?.first {
            switch touchEvent.phase {
                case .ended, .cancelled, .stationary:
                    //here, start playing if needed
                    startPlaying()                    
                default:
                    break
            }
        }
    }
    

    And on your "sliderTap" selector method:

    @objc func sliderTap() {
        //pause the player, if you want
        audioPlayer?.pause()
    }
    

    Suggestion: set the player "currentTime" before starting to play:

    private func startPlaying() {
        audioPlayer?.currentTime = Double(slider.value)
        audioPlayer?.play()
    }
    
    0 讨论(0)
  • 2020-12-03 21:48

    I completed @DWilliames solution for a UISlider subclass containing minimum and maximumValueImages.

    Additionally I implemented a functionality for user touches in the areas outside the trackArea (means either the area around the minimum or the maximumValueImage). Touching these areas moves the slider/changes the value in intervals.

    - (void) tapAndSlide: (UILongPressGestureRecognizer*) gesture {
        CGPoint touchPoint = [gesture locationInView: self];
        CGRect trackRect = [self trackRectForBounds: self.bounds];
        CGFloat thumbWidth = [self thumbRectForBounds: self.bounds trackRect: trackRect value: self.value].size.width;
        CGRect trackArea = CGRectMake(trackRect.origin.x, 0, trackRect.size.width, self.bounds.size.height);
        CGFloat value;
    
    if (CGRectContainsPoint(trackArea, touchPoint)) {
        if (touchPoint.x <= trackArea.origin.x + thumbWidth/2.0) {
            value = self.minimumValue;
        }
        else if (touchPoint.x >= trackArea.origin.x + trackArea.size.width - thumbWidth/2.0) {
            value = self.maximumValue;
        }
        else {
            CGFloat percentage = (touchPoint.x - trackArea.origin.x - thumbWidth/2.0)/(trackArea.size.width - thumbWidth);
            CGFloat delta = percentage*(self.maximumValue - self.minimumValue);
            value = self.minimumValue + delta;
        }
    
        if (value != self.value) {
            if (gesture.state == UIGestureRecognizerStateBegan) {
                [UIView animateWithDuration: 0.2 delay: 0 options: UIViewAnimationOptionCurveEaseInOut animations: ^{
                    [self setValue: value animated: YES];
    
                } completion: ^(BOOL finished) {
                    [self sendActionsForControlEvents: UIControlEventValueChanged];
                }];
            }
            else {
                [self setValue: value animated: YES];
                [self sendActionsForControlEvents: UIControlEventValueChanged];
            }
        }
    }
    else {
        if (gesture.state == UIGestureRecognizerStateBegan) {
            if (touchPoint.x <= trackArea.origin.x) {
                if (self.value == self.minimumValue) return;
                value = self.value - 1.5;
            }
            else {
                if (self.value == self.maximumValue) return;
                value = self.value + 1.5;
            }
            CGFloat duration = 0.1;
            [UIView animateWithDuration: duration delay: 0 options: UIViewAnimationOptionCurveEaseInOut animations: ^{
                [self setValue: value animated: YES];
            } completion: ^(BOOL finished) {
                [self sendActionsForControlEvents: UIControlEventValueChanged];
            }];
        }
    }
    }
    
    0 讨论(0)
提交回复
热议问题