Как я могу отслеживать, когда значение изменилось И когда перетаскивание остановлено? - PullRequest
0 голосов
/ 19 мая 2018

По своему опыту работы с UISlider я создаю IBAction для события ValueChanged.Теперь, в зависимости от того, как я установил isContinuous, он срабатывает либо при изменении значения, либо при остановке перетаскивания.Моя проблема в том, что мне нужно отслеживать ОБА сценарии, но я не могу установить isContinuous в обоих направлениях.

Можно ли как-то отследить изменение значения и когда пользователь прекращает перетаскивание?Я хочу обновить счетчик, когда значение меняется на непрерывное, а также я хочу обновить данные, когда перетаскивание останавливается.Я не хочу обновлять данные при каждом изменении значения, так как это приводит к чрезмерным накладным расходам.Я попробовал несколько других действий, но ни одно из них не было вызвано, когда перетаскивание останавливается.

Ответы [ 2 ]

0 голосов
/ 04 апреля 2019

Я очень поддерживаю принятый ответ Аллена Р ;это правильно на каждом уровне и не только дает удовлетворительный ответ, но и выходит за рамки, обучая тех, кто не сталкивался и не думал о объединении событий в единую форму уведомлений при разработке приложений для iOS.

Для тех из вас, кто, как и я, нуждался в простом решении в одном месте и не хочет вмешиваться в игру с Timer (s), вот решение, которое я придумал для выполнения некоторого кода после пользователь снимает палец с UISlider:

@objc private func rssiSliderChanged(sender: UISlider?) {
    // Value has changed.

    DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in
        let activeRecognisers = sender?.gestureRecognizers?.filter { $0.state == .changed }
        guard activeRecognisers?.isEmpty ?? true else { return }

        // Code to execute when the user finishes changing the UISlider's value.
    }
}

Пояснение : UIGestureRecognizer вызывает нашу цель, когда пользователь перетаскивает UISlider,Таким образом, для каждого изменения мы ставим в очередь замыкание, которое будет выполнено позже, и каждый раз проверяем, есть ли в ползунке активный распознаватель.Если нет, тогда жест перетаскивания завершен, и мы можем с уверенностью предположить, что пользователь поднял палец.

0 голосов
/ 21 мая 2018

Термин для решения, которое вы пытаетесь найти, называется «Debouncing».Идея состоит в том, что вы объединяете частые вызовы одного и того же метода и выполняете метод только после того, как вызовы прекратились в течение определенного периода времени.Debouncing - отличный способ улучшить пользовательский опыт, когда вы быстро принимаете большую часть пользовательского ввода и должны выполнять относительно большую нагрузку на этот ввод.Выполнение работы только после того, как пользователь завершил ввод, спасает процессор от чрезмерной работы и замедляет работу приложения.В некоторых примерах можно перемещать представление, когда пользователь прокручивает страницу, обновлять представление таблицы, когда пользователь вводит поисковый запрос, или совершать сетевые вызовы после серии нажатий кнопок.

Пример, показывающий, как его можно реализовать.с UISlider показано ниже на детской площадке.Вы можете скопировать и вставить пример в пустую игровую площадку, чтобы попробовать.

//: A UIKit based Playground for presenting user interface

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {

    // Timer to record the length of time between debounced calls
    var timer: Timer? = nil

    override func loadView() {
        super.loadView()

        let view = UIView()
        view.backgroundColor = .white

        // Set up the slider
        let slider = UISlider(frame: CGRect(x: 100, y: 100, width: 200, height: 50))
        slider.minimumValue = 0
        slider.maximumValue = 100
        slider.isContinuous = true
        slider.addTarget(self, action: #selector(sliderValueDidChange(_:)), for: .valueChanged)
        self.view.addSubview(slider)
    }

    @objc func sliderValueDidChange(_ sender: UISlider) {
        // Coalesce the calls until the slider valude has not changed for 0.2 seconds
        debounce(seconds: 0.2) {
            print("slider value: \(sender.value)")
        }
    }

    // Debounce function taking a time interval to wait before firing after user input has stopped
    // and a function to execute when debounce has stopped being called for a period of time.
    func debounce(seconds: TimeInterval, function: @escaping () -> Swift.Void ) {
        timer?.invalidate()
        timer = Timer.scheduledTimer(withTimeInterval: seconds, repeats: false, block: { _ in
            function()
        })
    }
}

// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
PlaygroundPage.current.needsIndefiniteExecution = true

Суть примера в следующем:

func debounce(seconds: TimeInterval, function: @escaping () -> Swift.Void ) {
    timer?.invalidate()
    timer = Timer.scheduledTimer(withTimeInterval: seconds, repeats: false, block: { _ in
        function()
    })
}

Здесь каждый раз debounceвызывается, делает недействительным таймер и затем планирует новый таймер для вызова переданного function. Это гарантирует, что function не вызывается до тех пор, пока не пройдет достаточно времени, чтобы не сделать таймер недействительным.

...