touchMoved не вызывается для настраиваемого распознавания жестов

#ios #swift #uigesturerecognizer

#iOS #swift #пользовательский распознаватель

Вопрос:

Я новичок в разработке iOS. В настоящее время мне нужно разработать ForceTouchGestureRecognizer с 2-секундной активацией при запуске пользователем touch

Но состояние для жеста принудительного касания очень хорошо передается после добавления holdFor > minimumPressDuration

Кажется, что .changed состояние не может быть достигнуто

Есть вывод на консоль

введите описание изображения здесь

Ниже приведен код для класса ForceTouchGestureRecognizer

 import UIKit
import UIKit.UIGestureRecognizerSubclass

class ForceTouchGestureRecognizer: UIGestureRecognizer {

var minimumValue: CGFloat = 0 // Value between 0.0 - 1.0 that needs to be reached before gesture begins
var tolerance: CGFloat = 1 // Once force drops below maxValue - tolerance, the gesture ends
var minimumPressDuration: Int = 1000

private(set) var forceValue: CGFloat? // value between 0.0 - 1.0
private var maxValue: CGFloat = 0
private var touchStartTime: Int = 0

override func reset() {
    super.reset()
    forceValue = nil
    maxValue = 0
    minimumPressDuration = 1500
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesBegan(touches, with: event)
    if #available(iOS 9.0, *) {
        if touches.count != 1 {
            state = .failed
        }
        touchStartTime = Int(NSDate().timeIntervalSince1970 * 1000)
    }
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesMoved(touches, with: event)
    if #available(iOS 9.0, *) {
        let touch = touches.first!
        let value = touch.force / touch.maximumPossibleForce
        let holdFor = Int(NSDate().timeIntervalSince1970 * 1000) - touchStartTime

        if state == .possible {
            if value > minimumValue amp;amp; holdFor > minimumPressDuration {
                self.state = .began
            }
        } else {
            if value < (maxValue - tolerance) {
                state = .ended
            } else {
                maxValue = max(self.forceValue ?? 0, maxValue)
                self.forceValue = value
                state = .changed
            }
        }
    }
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesCancelled(touches, with: event)
    if state == .began || state == .changed {
        state = .cancelled
    }
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesEnded(touches, with: event)
    if state == .began || state == .changed {
        state = .ended
    }
}

}
  

Источник: https://github.com/ashleymills/ForceTouchGestureRecognizer.swift/blob/master/ForceTouchGestureRecognizer.swift


Обновлено 17 марта 2019

Согласно ответу Craz1k0ek. Я думаю, будет неплохо опубликовать мой обработанный код здесь

 import UIKit.UIGestureRecognizerSubclass

class ForceTouchGestureRecognizer: UIGestureRecognizer {

var minimumValue: CGFloat = 0 // Value between 0.0 - 1.0 that needs to be reached before gesture begins
var tolerance: CGFloat = 1 // Once force drops below maxValue - tolerance, the gesture ends
var minimumPressDuration: TimeInterval = 1.5

private(set) var forceValue: CGFloat? // value between 0.0 - 1.0
private var maxValue: CGFloat = 0
private var touchStartTime: TimeInterval?

override func reset() {
    super.reset()
    forceValue = nil
    maxValue = 0
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesBegan(touches, with: event)
    if #available(iOS 9.0, *) {
        if touches.count != 1 {
            state = .failed
        }
        touchStartTime = Date().timeIntervalSince1970
    }
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
    guard touchStartTime != nil else { return }

    super.touchesMoved(touches, with: event)
    if #available(iOS 9.0, *) {
        let touch = touches.first!
        let value = touch.force / touch.maximumPossibleForce
        let holdFor = NSDate().timeIntervalSince1970 - touchStartTime!

        if holdFor > minimumPressDuration {
            if state == .possible {
                if value > minimumValue {
                    self.state = .began
                }
            } else {
                if value < (maxValue - tolerance) {
                    state = .ended
                } else {
                    maxValue = max(self.forceValue ?? 0, maxValue)
                    self.forceValue = value
                    state = .changed
                }
            }
        }
    }
}

override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesCancelled(touches, with: event)
    if state == .began || state == .changed {
        state = .cancelled
    }
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesEnded(touches, with: event)
    if state == .began || state == .changed {
        state = .ended
    }
}

}
  

Ответ №1:

Я действительно хочу процитировать документацию:

Особые соображения Подклассы UIGestureRecognizer должны использовать версию свойства state для чтения и записи. Они получают это повторное объявление при импорте заголовочного файла UIGestureRecognizerSubclass.h…

Я упростил ваш распознаватель жестов, чтобы он активировался через две секунды. Обратите внимание, что я вижу некоторую несогласованность в вашем коде: по умолчанию minimumPressDuration установлено значение 1000 по умолчанию, однако reset() устанавливает значение в 1500 , что также может объяснить некоторые особенности вашего поведения.

 class ForceTouchRecognizer: UIGestureRecognizer {

    // The minimum time the touch should take
    private let minimumTouchTime: TimeInterval = 2.0
    // The start time of the touch
    private var touchStartTime: TimeInterval?

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        // Set the start time
        touchStartTime = Date().timeIntervalSince1970
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        // If there is no start time, how is this possible, don't do anything
        guard touchStartTime != nil else { return }

        // Check if the minimumTouchTime has passed since the start time
        if(Date().timeIntervalSince1970 - touchStartTime! > minimumTouchTime) {
            print("I should perform some action now!")
        }
    }

    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
        // Make sure the start time is reset
        touchStartTime = nil
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        // Make sure the start time is reset
        touchStartTime = nil
    }

}
  

Я бы также рекомендовал взглянуть на класс UILongPressGestureRecognizer. У этого также может быть некоторая функциональность, которая вам нужна.

Комментарии:

1. Большое спасибо. Я попробовал ваш код, и он работает! Я думаю, что ключ в том, чтобы убрать условие «holdFor> minimumPressDuration» из проверки состояния.