У меня есть кнопка, которую я нажимаю, и появляется пользовательское предупреждение с обычный 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 (он красный), и посмотрите, где находится thumbRect
после того, как я передвинул его к точке 10 миль.
// 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 и одинаково влево/вправо для вашего значения dy.
Надеюсь это поможет.
Слишком большой? Вы можете установить вставки, как вам нравится. Просто измените значения dx
и dy
в приведенном выше коде на что-то меньшее (например, -5, -10).
Я создал подкласс UISlider и играл со значениями dx и dy, но никакой разницы. Спасибо за попытку.
Я нашел ответ, я собираюсь опубликовать его
Я нашел ключ к проблеме здесь, в этот ответ. @PaulaHasstenteufel сказал
// 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
@objc func onSliderValChanged(sender: UISlider, event: UIEvent { }
UIPanGetstureRecognizer
1- используйте UIPanGestureRecognizer
и включите userInteraction для UIView
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(draggedView(_:)))
clearView.isUserInteractionEnabled = true
clearView.addGestureRecognizer(panGesture)
2- ползунок, я закомментировал его addTarget для события .valueChanged
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)
}
Это было бы велико. Спасибо за предложение, хотя