我创建了一个滑块(用作视频控制,就像 YouTube 底部的那样)并设置最大值(持续时间)和最小值。然后使用
SeekToTime
更改当前时间。现在,用户可以滑动滑块来更改值
我想要实现的是让用户点击滑块上的任意位置并设置视频的当前时间。
我从这个答案中得到了一种方法,我尝试将其应用到我的案例中,但无法使其发挥作用
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()
函数。
我做错了什么?或者有更好的方法来实现我想要实现的目标吗?
看起来您需要根据上面的代码示例在 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)
}
}
似乎只是子类化 UISlider 并始终返回 true 到 beginTracking 就可以产生所需的效果。
iOS 10 和 Swift 3
class CustomSlider: UISlider {
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
return true
}
}
随后在代码中使用 CustomSlider 而不是 UISlider。
我喜欢这种方法
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()
但是,我实现了 pteofil,因为我已经附加了一个操作到
valueChanged
,所以我在跟踪方面遇到了问题。
我正在查看 Adam 的答案,我注意到他也做了一个非常好的实现,只是我一般不喜欢添加手势识别器。所以我结合了两个答案(有点),现在我都可以使用滑动来更改值(因此触发
valueChanged
以及点击滑块:
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
}
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
}
希望它会有一些用处并满足您的需要。
这是我的代码,基于“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)
}
}
pteofil 的答案应该是这里接受的答案。
以下是Xamarin的解决方案,供大家感兴趣:
public override bool BeginTracking(UITouch uitouch, UIEvent uievent)
{
return true;
}
正如 pteofil 提到的,您需要对 UISlider 进行子类化才能使其工作。
因此,将问题视为一个滑块,其值可以通过滑动“拇指”或通过点击整个滑块来更改,并将此页面上已有的许多答案和评论汇总在一起,我们可以整齐地表达解决方案如下:
class MySlider: UISlider {
override init(frame: CGRect) {
super.init(frame:frame)
config()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
config()
}
func config() {
let tap = UITapGestureRecognizer(target: self, action: #selector(tapped))
addGestureRecognizer(tap)
}
@objc func tapped(_ tapper: UITapGestureRecognizer) {
let pointTappedX = tapper.location(in: self).x
let positionOfSliderX = bounds.origin.x + 15
let widthOfSlider = bounds.size.width - 30
let newX = (pointTappedX - positionOfSliderX) / widthOfSlider
let newValue = newX * CGFloat(maximumValue) + (1 - newX) * CGFloat(minimumValue)
setValue(Float(newValue), animated: true)
sendActions(for: .valueChanged)
}
}
对我来说,@pietor 的 answer 仅在触摸移动足以触发值更改时才有效。这在实际设备上可能是正确的,但在模拟器中,它对单个触摸事件没有任何作用。下面的代码对我来说效果更好,并且当拇指移动到触摸位置时不那么生涩:
class CustomSlider : UISlider
{
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
let track = trackRect(forBounds: bounds)
let thumb = thumbRect(forBounds: bounds, trackRect: track, value: 0.0)
let pos = touch.location(in: self)
let normalizedValue = Float((pos.x - track.minX - thumb.width / 2.0) / (track.width - thumb.width))
self.setValue(minimumValue + (maximumValue - minimumValue) * normalizedValue, animated: true)
return true
}
}
最简单的解决方案可能是使用“内部修饰”操作,通过界面生成器进行连接。
@IBAction func finishedTouch(_ sender: UISlider) {
finishedMovingSlider(sender)
}
当您的手指离开手机屏幕时,就会调用此功能。