【问题标题】:Tap on UISlider to Set the Value点击 UISlider 设置值
【发布时间】:2016-04-09 17:47:16
【问题描述】:

我创建了一个滑块(作为视频的控制操作,就像底部的 YouTube 一样)并设置了最大值(持续时间)和最小值。然后使用SeekToTime 更改当前时间。现在,用户可以滑动滑块的拇指来改变值

我想要实现的是让用户点击滑块上的任意位置并设置视频的当前时间。

我从this answer 那里得到了一个方法,我尝试将它应用到我的案例中,但无法成功

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)      
 }
}

我认为这应该可行,但没有运气。然后我尝试通过尝试将“A”打印到日志来进行调试,但显然它没有进入sliderTapped() 函数。

我做错了什么?还是有更好的方法来实现我想要实现的目标?

【问题讨论】:

  • 你忘记设置手势识别器的代理了吗?

标签: ios swift uigesturerecognizer uislider uitapgesturerecognizer


【解决方案1】:

看起来您需要根据上面的代码示例在 viewDidLoad() 中实际初始化点击手势识别器。那里有评论,但我没有看到在任何地方创建识别器。

斯威夫特 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)      
    }
}

斯威夫特 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)
    }
}

【讨论】:

  • 这个答案比我发现的任何其他答案都好得多!谢谢!
  • action: "sliderTapped:" 的创建在 Xcode 8.2.1 中不再有效。我尝试了 Xcode 给我的建议,但仍然失败。有没有人知道?
  • 我想我找到了。对于 Swift 3,代码如下所示:let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tapBlurButton(_:)))
  • 滑块的两侧都有一个 15 像素的死区(不是真正的死区,因为滑块一旦到达这个“死区”就会停止移动),因此您应该增加 positionOfSlider。 x 15 和 widthOfSlider 减少 30。请注意,这需要考虑超出滑块值范围的点击,即如果用户在任一死区点击。如果您正在使用“捕捉”到整数值的滑块,则应使用 slider.setValue(Float(Int(newValue + 0.5))) 或以其他方式将值四舍五入为最接近的整数。
  • 效果很好,但它没有考虑滑块是否具有非零的最小值。这是正确的代码: let newValue = (((pointTapped.x - positionOfSlider.x) / widthOfSlider) * CGFloat(distanceSlider.maximumValue)) + ((1 - ((pointTapped.x - positionOfSlider.x) / widthOfSlider)) * CGFloat(distanceSlider.minimumValue))
【解决方案2】:

似乎只是继承 UISlider 并始终返回 true 到 beginTracking 会产生所需的效果。

iOS 10 和 Swift 3

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

之后在您的代码中使用 CustomSlider 而不是 UISlider。

【讨论】:

  • 简洁明了的答案。这现在应该是公认的答案
  • 如果您在手指释放时有自定义代码用于滑块,例如强制离散步骤,那么此代码也将在单击时调用该函数。也许这不是理想的效果,需要来自 myuiviews 的上述答案。但在大多数情况下,这会很好地工作!
  • 终于苹果 :)
【解决方案3】:

pteofil 的答案应该是这里接受的答案。

以下是 Xamarin 的解决方案,供所有感兴趣的人参考:

public override bool BeginTracking(UITouch uitouch, UIEvent uievent)
{
    return true;
}

正如 pteofil 提到的,您需要子类化 UISlider 才能使其工作。

【讨论】:

  • 您的回答有什么新变化?
【解决方案4】:

这是我的代码,基于“myuiviews”的答案。
我修复了原始代码的 2 个小“错误”。

1 - 点击 0 太难了,所以我让它更容易
2 - 滑动滑块的拇指一点点也会触发“tapGestureRecognizer”,使其返回到初始位置,所以我添加了一个最小距离过滤器来避免这种情况。

斯威夫特 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)
    }
}

【讨论】:

  • 我对 newValue 计算做了一点改动,以便在滑块不为零时满足滑块的最小值。 'code'newValue = (((pointTapped.x - positionOfSlider.x) * CGFloat(self.offerSliderView.maximumValue - self.offerSliderView.minimumValue) / widthOfSlider) + CGFloat(self.offerSliderView.minimumValue))'code'
【解决方案5】:

可能最简单的解决方案是,使用“内部修饰”操作,通过界面构建​​器进行连接。

@IBAction func finishedTouch(_ sender: UISlider) {

    finishedMovingSlider(sender)
}

只要您的手指离开手机屏幕,就会调用它。

【讨论】:

    【解决方案6】:

    我喜欢这种方法

    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)
        }
    }
    

    稍后只需调用

    let slider = UISlider()
    slider.addTapGesture()
    

    【讨论】:

      【解决方案7】:

      但是,我实现了 pteofil,因为我已经有一个附加到 valueChanged 的操作,所以我在跟踪方面遇到了问题。

      我正在查看 Adam 的答案,我注意到他也做了一个非常好的实现,除了我通常不喜欢添加手势识别器。所以我结合了这两个答案(有点),现在我都可以使用滑动来更改值(因此触发valueChanged 以及如果点击滑块:

      1. 我继承了UISlider,并覆盖了beginTracking 方法:
          override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
              return true //Without this, we don't get any response in the method below 
          }
      
      1. 然后我覆盖了touchEnded 方法(我想你也可以覆盖其他状态,但这似乎是最符合逻辑的方法)
          override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
              guard let touch = touches.first else { return }
              let location = touch.location(in: self)
              let conversion = minimumValue + Float(location.x / bounds.width) * maximumValue
              setValue(conversion, animated: false)
              sendActions(for: .valueChanged) //Add this because without this it won't work.
          }
      

      我在其他地方也有我的标准 IBAction(这与上面没有直接关系,但我添加它是为了提供完整的上下文):

          @IBAction func didSkipToTime(_ sender: UISlider) {
              print("sender value: \(sender.value)") // Do whatever in this method
          }
      

      希望它会有所帮助并满足您的需求。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多