iOS - 如何让单独的 UIView 控制 UISlider 的 thumbRect
iOS -How to have a separate UIView take control of a UISlider's thumbRect
我有一个按钮,按下后会出现一个自定义警报,里面有一个 normal UISlider。我还有一些其他视图和标签显示滑块上方和后面的距离等。发生的事情是滑块的 thumbRect 在触摸时没有滑动到很好。尝试滑动它时我必须非常准确,而且它看起来有问题。我想要做的是在它前面添加一个清晰的 UIView(以及它上面的其他视图和标签),并让清晰的 UIView 使用 UIGestureRecognizer
控制滑块。
这是目前的设置。 clearView 是我想用来控制滑块的东西。现在它位于所有内容的后面,我将 clearView 涂成红色,以便在示例中可见
bgView.addSubview(slider) // slider added to bgView
let trackRect = slider.trackRect(forBounds: slider.frame)
let thumbRect = slider.thumbRect(forBounds: slider.bounds, trackRect: trackRect, value: slider.value)
milesLabel.center = CGPoint(x: thumbRect.midX, y: slider.frame.origin.y - 55)
bg.addSubview(milesLabel) // milesLabel added to bgView
// ** all the other views you see are added to the bgView and aligned with the thumbRect's center **
let milesLabelRect = bgView.convert(milesLabel.frame, to: self.view)
let sliderRect = bgView.convert(slider.frame, to: self.view)
let topOfMilesLabel = milesLabelRect.origin.y
let bottomOfSlider = sliderRect.maxY
let distance = bottomOfSlider - topOfMilesLabel
clearView.frame = CGRect(x: milesLabel.frame.minX, y: milesLabel.frame.minY, width: milesLabel.frame.width, height: distance)
bgView.addSubview(clearView) // clearView added to bgView
bgView.insertSubview(clearView, at: 0)
当我滑动滑块时,包括 clearView 在内的所有内容都成功滑动,但当然 thumbRect
仍然控制着所有内容。
@objc func onSliderValChanged(sender: UISlider, event: UIEvent) {
slider.value = round(sender.value)
UIView.animate(withDuration: 0.25, animations: {
self.slider.layoutIfNeeded()
let trackRect = sender.trackRect(forBounds: sender.frame)
let thumbRect = sender.thumbRect(forBounds: sender.bounds, trackRect: trackRect, value: sender.value)
self.milesLabel.center = CGPoint(x: thumbRect.midX, y: sender.frame.origin.y - 55)
// ** all the other views are aligned with the thumbRect's center as it slides **
self.clearView.frame.origin = self.milesLabel.frame.origin
})
}
想法是将 clearView 移动到所有内容的前面,并用它来控制 thumbRect(不要忘记它只是示例中的红色)。
bgView.bringSubviewToFront(clearView)
// clearView.backgroundColor = UIColor.clear
我尝试使用 UIPanGesture
和 UILongPressGestureRecognizer
让 clearView 控制滑块,但是当我滑动滑块时只有 thumbRect 滑动,clearView 保持静止。查看 clearView(红色)的位置,并在我将其滑到 10 英里点后查看 thumbRect
的位置。
// separately tried a UILongPressGestureRecognizer too
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
clearView.isUserInteractionEnabled = true
clearView.addGestureRecognizer(panGesture)
@objc func draggedView(_ sender: UIPanGestureRecognizer) {
if slider.isHighlighted { return }
let point = sender.location(in: slider)
let percentage = Float(point.x / slider.frame.width)
let delta = percentage * (slider.maximumValue - slider.minimumValue)
let value = slider.minimumValue + delta
slider.setValue(value, animated: true)
}
如果我玩得够多,clearView 有时会与 thumbRect 一起滑动,但它们肯定没有对齐,而且非常容易出错。我也失去了对与 thumbRect 对齐的所有其他视图的控制。
如何将控制权从滑块的 thumbRect 传递给 clearView?
你可能已经下定决心这样做了,这没关系。但我有一个可能适合您需要的快速替代方案。
如果将播放滑块子类化并覆盖 point()
方法,您可以增加滑块拇指的表面积。
class CustomPlaybackSlider: UISlider {
// Will increase target surface area for thumb.
override public func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
var bounds: CGRect = self.bounds
// Set the inset to a negative value by how much you want the surface area to increase by.
bounds = bounds.insetBy(dx: -10, dy: -15)
return bounds.contains(point)
}
}
可能会为您简化事情。虽然,这种方法的唯一缺点是触摸表面积对于您的 dx 值将同样扩展 down/up,对于您的 dy 值将同样扩展 left/right。
希望对您有所帮助。
我在this answer这里找到了问题的关键。 @PaulaHassteneufel 说
// 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)
我要做的是
- 使用
UIPanGestureRecognizer
- 注释掉 UISlider 的目标操作
- 在步骤 4 中使用目标操作的选择器方法中的代码,而不是在
@objc func onSliderValChanged(sender: UISlider, event: UIEvent { }
中使用它
- 将步骤 3 中的代码添加到新函数中
- 将步骤 5 中的函数添加到
UIPanGetstureRecognizer
方法的底部
1- 使用 UIPanGestureRecognizer
并为 UIView 启用 userInteraction
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
clearView.isUserInteractionEnabled = true
clearView.addGestureRecognizer(panGesture)
2- Slider,我注释掉它是 .valueChanged
事件的 addTarget
let slider: UISlider = {
let slider = UISlider()
slider.translatesAutoresizingMaskIntoConstraints = false
slider.minimumValue = 1
slider.maximumValue = 5
slider.value = 1
slider.isContinuous = true
slider.maximumTrackTintColor = UIColor(white: 0, alpha: 0.3)
// ** COMMENT THIS OUT **
//slider.addTarget(self, action: #selector(onSliderValChanged(sender:event:)), for: .valueChanged)
return slider
}()
3- 从上面的 targetAction 使用的选择器方法中获取代码,而不是在步骤 4 中插入该代码
@objc func onSliderValChanged(sender: UISlider, event: UIEvent) {
/*
use this code in step-4 instead
slider.value = round(sender.value)
UIView.animate(withDuration: 0.25, animations: {
self.slider.layoutIfNeeded()
let trackRect = sender.trackRect(forBounds: sender.frame)
let thumbRect = sender.thumbRect(forBounds: sender.bounds, trackRect: trackRect, value: sender.value)
self.milesLabel.center = CGPoint(x: thumbRect.midX, y: sender.frame.origin.y - 55)
// ** all the other views are aligned with the thumbRect's center as it slides **
self.clearView.frame.origin = self.milesLabel.frame.origin
})
*/
}
4- 创建一个新方法并插入步骤 3 中的代码
func sliderValueChanged(_ sender: UISlider, value: Float) {
// round the value for smooth sliding
sender.value = round(value)
UIView.animate(withDuration: 0.25, animations: {
self.slider.layoutIfNeeded()
let trackRect = sender.trackRect(forBounds: sender.frame)
let thumbRect = sender.thumbRect(forBounds: sender.bounds, trackRect: trackRect, value: sender.value)
self.milesLabel.center = CGPoint(x: thumbRect.midX, y: sender.frame.origin.y - 55)
// ** all the other views are aligned with the thumbRect's center as it slides **
self.clearView.frame.origin = self.milesLabel.frame.origin
})
}
5- 我使用了与 UIGestureRecognizer 相同的代码,但注释掉了 setValue 函数,而是使用了步骤 4 中的函数
@objc fileprivate func draggedView(_ sender: UIPanGestureRecognizer) {
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
// I had to comment this out because the siding was to abrupt
//slider.setValue(value, animated: true)
// ** CALLING THIS FUNCTION FROM STEP-3 IS THE KEY TO GET THE UIView TO CONTROL THE SLIDER **
sliderValueChanged(slider, value: value)
}
我有一个按钮,按下后会出现一个自定义警报,里面有一个 normal UISlider。我还有一些其他视图和标签显示滑块上方和后面的距离等。发生的事情是滑块的 thumbRect 在触摸时没有滑动到很好。尝试滑动它时我必须非常准确,而且它看起来有问题。我想要做的是在它前面添加一个清晰的 UIView(以及它上面的其他视图和标签),并让清晰的 UIView 使用 UIGestureRecognizer
控制滑块。
这是目前的设置。 clearView 是我想用来控制滑块的东西。现在它位于所有内容的后面,我将 clearView 涂成红色,以便在示例中可见
bgView.addSubview(slider) // slider added to bgView
let trackRect = slider.trackRect(forBounds: slider.frame)
let thumbRect = slider.thumbRect(forBounds: slider.bounds, trackRect: trackRect, value: slider.value)
milesLabel.center = CGPoint(x: thumbRect.midX, y: slider.frame.origin.y - 55)
bg.addSubview(milesLabel) // milesLabel added to bgView
// ** all the other views you see are added to the bgView and aligned with the thumbRect's center **
let milesLabelRect = bgView.convert(milesLabel.frame, to: self.view)
let sliderRect = bgView.convert(slider.frame, to: self.view)
let topOfMilesLabel = milesLabelRect.origin.y
let bottomOfSlider = sliderRect.maxY
let distance = bottomOfSlider - topOfMilesLabel
clearView.frame = CGRect(x: milesLabel.frame.minX, y: milesLabel.frame.minY, width: milesLabel.frame.width, height: distance)
bgView.addSubview(clearView) // clearView added to bgView
bgView.insertSubview(clearView, at: 0)
当我滑动滑块时,包括 clearView 在内的所有内容都成功滑动,但当然 thumbRect
仍然控制着所有内容。
@objc func onSliderValChanged(sender: UISlider, event: UIEvent) {
slider.value = round(sender.value)
UIView.animate(withDuration: 0.25, animations: {
self.slider.layoutIfNeeded()
let trackRect = sender.trackRect(forBounds: sender.frame)
let thumbRect = sender.thumbRect(forBounds: sender.bounds, trackRect: trackRect, value: sender.value)
self.milesLabel.center = CGPoint(x: thumbRect.midX, y: sender.frame.origin.y - 55)
// ** all the other views are aligned with the thumbRect's center as it slides **
self.clearView.frame.origin = self.milesLabel.frame.origin
})
}
想法是将 clearView 移动到所有内容的前面,并用它来控制 thumbRect(不要忘记它只是示例中的红色)。
bgView.bringSubviewToFront(clearView)
// clearView.backgroundColor = UIColor.clear
我尝试使用 UIPanGesture
和 UILongPressGestureRecognizer
让 clearView 控制滑块,但是当我滑动滑块时只有 thumbRect 滑动,clearView 保持静止。查看 clearView(红色)的位置,并在我将其滑到 10 英里点后查看 thumbRect
的位置。
// separately tried a UILongPressGestureRecognizer too
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
clearView.isUserInteractionEnabled = true
clearView.addGestureRecognizer(panGesture)
@objc func draggedView(_ sender: UIPanGestureRecognizer) {
if slider.isHighlighted { return }
let point = sender.location(in: slider)
let percentage = Float(point.x / slider.frame.width)
let delta = percentage * (slider.maximumValue - slider.minimumValue)
let value = slider.minimumValue + delta
slider.setValue(value, animated: true)
}
如果我玩得够多,clearView 有时会与 thumbRect 一起滑动,但它们肯定没有对齐,而且非常容易出错。我也失去了对与 thumbRect 对齐的所有其他视图的控制。
如何将控制权从滑块的 thumbRect 传递给 clearView?
你可能已经下定决心这样做了,这没关系。但我有一个可能适合您需要的快速替代方案。
如果将播放滑块子类化并覆盖 point()
方法,您可以增加滑块拇指的表面积。
class CustomPlaybackSlider: UISlider {
// Will increase target surface area for thumb.
override public func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
var bounds: CGRect = self.bounds
// Set the inset to a negative value by how much you want the surface area to increase by.
bounds = bounds.insetBy(dx: -10, dy: -15)
return bounds.contains(point)
}
}
可能会为您简化事情。虽然,这种方法的唯一缺点是触摸表面积对于您的 dx 值将同样扩展 down/up,对于您的 dy 值将同样扩展 left/right。
希望对您有所帮助。
我在this answer这里找到了问题的关键。 @PaulaHassteneufel 说
// 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)
我要做的是
- 使用
UIPanGestureRecognizer
- 注释掉 UISlider 的目标操作
- 在步骤 4 中使用目标操作的选择器方法中的代码,而不是在
@objc func onSliderValChanged(sender: UISlider, event: UIEvent { }
中使用它
- 将步骤 3 中的代码添加到新函数中
- 将步骤 5 中的函数添加到
UIPanGetstureRecognizer
方法的底部
1- 使用 UIPanGestureRecognizer
并为 UIView 启用 userInteraction
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
clearView.isUserInteractionEnabled = true
clearView.addGestureRecognizer(panGesture)
2- Slider,我注释掉它是 .valueChanged
事件的 addTarget
let slider: UISlider = {
let slider = UISlider()
slider.translatesAutoresizingMaskIntoConstraints = false
slider.minimumValue = 1
slider.maximumValue = 5
slider.value = 1
slider.isContinuous = true
slider.maximumTrackTintColor = UIColor(white: 0, alpha: 0.3)
// ** COMMENT THIS OUT **
//slider.addTarget(self, action: #selector(onSliderValChanged(sender:event:)), for: .valueChanged)
return slider
}()
3- 从上面的 targetAction 使用的选择器方法中获取代码,而不是在步骤 4 中插入该代码
@objc func onSliderValChanged(sender: UISlider, event: UIEvent) {
/*
use this code in step-4 instead
slider.value = round(sender.value)
UIView.animate(withDuration: 0.25, animations: {
self.slider.layoutIfNeeded()
let trackRect = sender.trackRect(forBounds: sender.frame)
let thumbRect = sender.thumbRect(forBounds: sender.bounds, trackRect: trackRect, value: sender.value)
self.milesLabel.center = CGPoint(x: thumbRect.midX, y: sender.frame.origin.y - 55)
// ** all the other views are aligned with the thumbRect's center as it slides **
self.clearView.frame.origin = self.milesLabel.frame.origin
})
*/
}
4- 创建一个新方法并插入步骤 3 中的代码
func sliderValueChanged(_ sender: UISlider, value: Float) {
// round the value for smooth sliding
sender.value = round(value)
UIView.animate(withDuration: 0.25, animations: {
self.slider.layoutIfNeeded()
let trackRect = sender.trackRect(forBounds: sender.frame)
let thumbRect = sender.thumbRect(forBounds: sender.bounds, trackRect: trackRect, value: sender.value)
self.milesLabel.center = CGPoint(x: thumbRect.midX, y: sender.frame.origin.y - 55)
// ** all the other views are aligned with the thumbRect's center as it slides **
self.clearView.frame.origin = self.milesLabel.frame.origin
})
}
5- 我使用了与 UIGestureRecognizer 相同的代码,但注释掉了 setValue 函数,而是使用了步骤 4 中的函数
@objc fileprivate func draggedView(_ sender: UIPanGestureRecognizer) {
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
// I had to comment this out because the siding was to abrupt
//slider.setValue(value, animated: true)
// ** CALLING THIS FUNCTION FROM STEP-3 IS THE KEY TO GET THE UIView TO CONTROL THE SLIDER **
sliderValueChanged(slider, value: value)
}