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

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

    This works for me in iOS 13.6 & 14.0
    No need to add gesture only override beginTracking function like this :

    @objc
    private func sliderTapped(touch: UITouch) {
        let point = touch.location(in: self)
        let percentage = Float(point.x / self.bounds.width)
        let delta = percentage * (self.maximumValue - self.minimumValue)
        let newValue = self.minimumValue + delta
        if newValue != self.value {
            value = newValue
            sendActions(for: .valueChanged)
        }
    }
    
    override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        sliderTapped(touch: touch)
        return true
    }
    
    0 讨论(0)
  • 2020-12-03 21:26

    At the risk of being chastised by the iOS pure community...

    Here is a solution for Xamarin iOS C# converted from David Williames Answer.

    Sub class UISlider:

    [Register(nameof(UISliderCustom))]
    [DesignTimeVisible(true)]
    public class UISliderCustom : UISlider
    {
    
        public UISliderCustom(IntPtr handle) : base(handle) { }
    
        public UISliderCustom()
        {
            // Called when created from code.
            Initialize();
        }
    
        public override void AwakeFromNib()
        {
            // Called when loaded from xib or storyboard.
            Initialize();
        }
    
        void Initialize()
        {
            // Common initialization code here.
    
            var longPress = new UILongPressGestureRecognizer(tapAndSlide);
            longPress.MinimumPressDuration = 0;
            //longPress.CancelsTouchesInView = false;
            this.AddGestureRecognizer(longPress);
            this.UserInteractionEnabled = true;
    
        }
    
        private void tapAndSlide(UILongPressGestureRecognizer gesture)
        {
            System.Diagnostics.Debug.WriteLine($"{nameof(UISliderCustom)} RecognizerState {gesture.State}");
    
            // need to propagate events down the chain
            // I imagine iOS does something similar
            // for whatever recogniser on the thumb control
            // It's not enough to set CancelsTouchesInView because
            // if clicking on the track away from the thumb control
            // the thumb gesture recogniser won't pick it up anyway
            switch (gesture.State)
            {
                case UIGestureRecognizerState.Cancelled:
                    this.SendActionForControlEvents(UIControlEvent.TouchCancel);
                    break;
    
                case UIGestureRecognizerState.Began:
                    this.SendActionForControlEvents(UIControlEvent.TouchDown);
                    break;
    
                case UIGestureRecognizerState.Changed:
                    this.SendActionForControlEvents(UIControlEvent.ValueChanged);                    
                    break;
    
                case UIGestureRecognizerState.Ended:
                    this.SendActionForControlEvents(UIControlEvent.TouchUpInside);
                    break;
    
                case UIGestureRecognizerState.Failed:
                    //?
                    break;
    
                case UIGestureRecognizerState.Possible:
                    //?
                    break;
    
            }
    
            var pt = gesture.LocationInView(this);
            var thumbWidth = CurrentThumbImage.Size.Width;
            var value = 0f;
    
            if (pt.X <= thumbWidth / 2)
            {
                value = this.MinValue;
            }
            else if (pt.X >= this.Bounds.Size.Width - thumbWidth / 2)
            {
                value = this.MaxValue;
            }
            else
            {
                var percentage = ((pt.X - thumbWidth / 2) / (this.Bounds.Size.Width - thumbWidth));
                var delta = percentage * (this.MaxValue - this.MinValue);
                value = this.MinValue + (float)delta;
            }
    
            if (gesture.State == UIGestureRecognizerState.Began)
            {               
                UIView.Animate(0.35, 0, UIViewAnimationOptions.CurveEaseInOut,
                    () =>
                    {
                        this.SetValue(value, true);
                    },
                    null);
            }
            else
            {
                this.SetValue(value, animated: false);
            }
    
        }
    
    }
    
    0 讨论(0)
  • 2020-12-03 21:29

    Here's my modification to the above:

    class TapUISlider: UISlider {
    
      func tapAndSlide(gesture: UILongPressGestureRecognizer) {
        let pt = gesture.locationInView(self)
        let thumbWidth = self.thumbRect().size.width
        var value: Float = 0
    
        if (pt.x <= self.thumbRect().size.width / 2) {
          value = self.minimumValue
        } else if (pt.x >= self.bounds.size.width - thumbWidth / 2) {
          value = self.maximumValue
        } else {
          let percentage = Float((pt.x - thumbWidth / 2) / (self.bounds.size.width - thumbWidth))
          let delta = percentage * (self.maximumValue - self.minimumValue)
          value = self.minimumValue + delta
        }
    
        if (gesture.state == UIGestureRecognizerState.Began) {
          UIView.animateWithDuration(0.35, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut,
            animations: {
              self.setValue(value, animated: true)
              super.sendActionsForControlEvents(UIControlEvents.ValueChanged)
            }, completion: nil)
        } else {
          self.setValue(value, animated: false)
          super.sendActionsForControlEvents(UIControlEvents.ValueChanged)
        }
      }
    
      func thumbRect() -> CGRect {
        return self.thumbRectForBounds(self.bounds, trackRect: self.bounds, value: self.value)
      }
    }
    
    0 讨论(0)
  • 2020-12-03 21:33

    To expand on the answer of Khang Azun- for swift 5 put the following in a UISlider custom class:

    override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        let percent = Float(touch.location(in: self).x / bounds.size.width)
        let delta = percent * (maximumValue - minimumValue)
    
        let newValue = minimumValue + delta
        self.setValue(newValue, animated: false)
        super.sendActions(for: UIControl.Event.valueChanged)
        return true
    }
    
    0 讨论(0)
  • 2020-12-03 21:33

    From Apple,

    https://developer.apple.com/forums/thread/108317

    Now this works fine on iOS 10 and iOS 11. You can slide as usual and thanks to the above code you can tap on slider and it slides automatically. However in iOS 12 this doesn't work. You have to force touch on it for tap to work

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

    I converted the answer provided by DWilliames to Swift

    Inside your viewDidAppear()

    let longPress                  = UILongPressGestureRecognizer(target: self.slider, action: Selector("tapAndSlide:"))
    longPress.minimumPressDuration = 0
    self.addGestureRecognizer(longPress)
    

    Class file

    class TapUISlider: UISlider
    {
        func tapAndSlide(gesture: UILongPressGestureRecognizer)
        {
            let pt           = gesture.locationInView(self)
            let thumbWidth   = self.thumbRect().size.width
            var value: Float = 0
    
            if (pt.x <= self.thumbRect().size.width / 2)
            {
                value = self.minimumValue
            }
            else if (pt.x >= self.bounds.size.width - thumbWidth / 2)
            {
                value = self.maximumValue
            }
            else
            {
                let percentage = Float((pt.x - thumbWidth / 2) / (self.bounds.size.width - thumbWidth))
                let delta      = percentage * (self.maximumValue - self.minimumValue)
    
                value          = self.minimumValue + delta
            }
    
            if (gesture.state == UIGestureRecognizerState.Began)
            {
                UIView.animateWithDuration(0.35, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut,
                animations:
                {
                    self.setValue(value, animated: true)
                    super.sendActionsForControlEvents(UIControlEvents.ValueChanged)
                },
                completion: nil)
            }
            else
            {
                self.setValue(value, animated: false)
            }
        }
    
        func thumbRect() -> CGRect
        {
            return self.thumbRectForBounds(self.bounds, trackRect: self.bounds, value: self.value)
        }
    }
    
    0 讨论(0)
提交回复
热议问题