IOS - Как сделать так, чтобы отдельный UIView контролировал thumbRect UISlider

У меня есть кнопка, которую я нажимаю, и появляется пользовательское предупреждение с обычный UISlider внутри него. У меня есть несколько других представлений и меток, которые показывают расстояние и т. д., которые находятся над и позади ползунка. Что происходит, так это то, что thumbRect ползунка плохо скользит при касании. Я должен быть очень точным, пытаясь сдвинуть его, и он кажется глючным. Что я хочу сделать, так это добавить четкий UIView перед ним (и другие представления и метки над ним) и сделать так, чтобы четкий UIView управлял ползунком с помощью UIGestureRecognizer.

Вот установка на данный момент. ClearView — это то, что я хочу использовать для управления ползунком. Сейчас это позади всего и Я покрасил clearView в красный цвет, чтобы он был виден в примере.

IOS - Как сделать так, чтобы отдельный UIView контролировал thumbRect UISlider

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 все еще контролирует все.

IOS - Как сделать так, чтобы отдельный UIView контролировал thumbRect UISlider

@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

IOS - Как сделать так, чтобы отдельный UIView контролировал thumbRect UISlider

Я попытался использовать UIPanGesture и UILongPressGestureRecognizer, чтобы clearView взял на себя управление ползунком, но когда я перемещаю ползунок, скользит только thumbRect, clearView остается неподвижным. Посмотрите, где находится clearView (он красный), и посмотрите, где находится thumbRect после того, как я передвинул его к точке 10 миль.

IOS - Как сделать так, чтобы отдельный UIView контролировал thumbRect UISlider

// 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?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
335
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Возможно, вы решили сделать это таким образом, и это нормально. Но у меня есть быстрая альтернатива для вас, которая может удовлетворить ваши потребности.

Если вы подклассифицируете ползунок воспроизведения и переопределите метод 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.

Надеюсь это поможет.

Это было бы велико. Спасибо за предложение, хотя

Lance Samaria 04.06.2019 22:13

Слишком большой? Вы можете установить вставки, как вам нравится. Просто измените значения dx и dy в приведенном выше коде на что-то меньшее (например, -5, -10).

jake 04.06.2019 22:18

Я создал подкласс UISlider и играл со значениями dx и dy, но никакой разницы. Спасибо за попытку.

Lance Samaria 04.06.2019 23:33

Я нашел ответ, я собираюсь опубликовать его

Lance Samaria 05.06.2019 00:23
Ответ принят как подходящий

Я нашел ключ к проблеме здесь, в этот ответ. @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)

Что я должен был сделать, это

  1. Используйте UIPanGestureRecognizer
  2. закомментировать целевое действие UISlider
  3. используйте код из метода селектора целевого действия на шаге 4 вместо его использования в @objc func onSliderValChanged(sender: UISlider, event: UIEvent { }
  4. возьмите код из шага 3 и добавьте его в новую функцию
  5. добавьте функцию из шага 5 в конец метода 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)
}

Другие вопросы по теме