Жест касания не обнаруживается при анимации движущегося объекта (UIView) - PullRequest
0 голосов
/ 23 января 2019

Я пытаюсь просто переместить UIView с помощью анимации. Вот мой пример кода.

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tap1(gesture:)))
    tapGesture.numberOfTapsRequired = 1
    tapGesture.numberOfTouchesRequired = 1
    self.tempView.addGestureRecognizer(tapGesture)

    self.tempView.frame = CGRect(x: self.tempView.frame.origin.x, y: 0, width: self.tempView.frame.width, height: self.tempView.frame.height)

    UIView.animate(withDuration: 15, delay: 0.0, options: [.allowUserInteraction, .allowAnimatedContent], animations: {
      self.tempView.frame = CGRect(x: self.tempView.frame.origin.x, y: UIScreen.main.bounds.height - 100, width: self.tempView.frame.width, height: self.tempView.frame.height)


    }) { (_) in

    }

Здесь я упоминаю метод жестов касания:

@objc func tap1(gesture: UITapGestureRecognizer) {
    print("tap with object")
}

А когда UIView анимирует и перемещается из одной позиции в другую, в это время жест касания не работает. это моя проблема. Пожалуйста, помогите мне.
Спасибо заранее.

1 Ответ

0 голосов
/ 23 января 2019

Хотя визуально просматривать изменения позиции он не делает этого.Если вы проверите его фрейм во время анимации, фрейм всегда будет вашим конечным фреймом.Таким образом, вы можете нажать его там, где будет его конечное положение.(Это также предполагает, что вы включили взаимодействие с пользователем во время анимации, которую вы сделали).

Что вам нужно для вашего случая, так это вручную переместить ваше представление с таймером.Это займет немного больше усилий, но может быть легко выполнимо.Таймер должен срабатывать с некоторыми хорошими FPS, такими как 60, и каждый раз, когда он запускает, кадр должен обновляться до его интерполированной позиции.

Чтобы выполнить интерполяцию, вы можете просто сделать это по компоненту:

func interpolateRect(from: CGRect, to: CGRect, scale: CGFloat) -> CGRect {
    return CGRect(x: from.minX + (to.minX - from.minX) * scale, y: from.minY + (to.minY - from.minY) * scale, width: from.width + (to.width - from.width) * scale, height: from.height + (to.height - from.height) * scale)
}

Масштаб обычно должен быть между 0 и 1. В большинстве случаев

Теперь вам нужно иметь метод для анимации с помощью таймера, например:

func animateFrame(to frame: CGRect, animationDuration duration: TimeInterval) {
    self.animationStartFrame = tempView.frame // Assign new values
    self.animationEndFrame = frame // Assign new values
    self.animationStartTime = Date() // Assign new values

    self.currentAnimationTimer.invalidate() // Remove current timer if any

    self.currentAnimationTimer = Timer.scheduledTimer(withTimeInterval: 1.0/60.0, repeats: true) { timer in
        let timeElapsed = Date().timeIntervalSince(self.animationStartTime)
        let scale = timeElapsed/duration
        self.tempView.frame = self.interpolateRect(from: self.animationStartFrame, to: self.animationEndFrame, scale: CGFloat(max(0.0, min(scale, 1.0))))
        if(scale >= 1.0) { // Animation ended. Remove timer
            timer.invalidate()
            self.currentAnimationTimer = nil
        }
     }
 }

Чтобы быть справедливым, этот код все еще может бытьсокращено, так как мы используем таймер с блоком:

func animateFrame(to frame: CGRect, animationDuration duration: TimeInterval) {
    let startFrame = tempView.frame
    let endFrame = frame
    let animationStartTime = Date()

    self.currentAnimationTimer.invalidate() // Remove current timer if any

    self.currentAnimationTimer = Timer.scheduledTimer(withTimeInterval: 1.0/60.0, repeats: true) { timer in
        let timeElapsed = Date().timeIntervalSince(animationStartTime)
        let scale = timeElapsed/duration
        self.tempView.frame = self.interpolateRect(from: startFrame, to: endFrame, scale: CGFloat(max(0.0, min(scale, 1.0))))
        if(scale >= 1.0) { // Animation ended. Remove timer
            timer.invalidate()
            if(timer === self.currentAnimationTimer) {
                self.currentAnimationTimer = nil // Remove reference only if this is the current timer
            }
        }
     }
 }

Таким образом, нам не нужно хранить столько значений в нашем классе, но хранить все внутри метода.

Это может бытьИнтересно упомянуть, как сделать смягчение.Хитрость заключается только в том, чтобы манипулировать нашим scale.Рассмотрим что-то вроде этого:

func easeInScaleFromLinearScale(_ scale: CGFloat, factor: CGFloat = 0.2) -> CGFloat {
    return pow(scale, 1.0+factor)
}
func easeOutScaleFromLinearScale(_ scale: CGFloat, factor: CGFloat = 0.2) -> CGFloat {
    return pow(scale, 1.0/(1.0+factor))
}

Теперь все, что нам нужно сделать, это использовать его при интерполяции:

self.tempView.frame = self.interpolateRect(from: startFrame, to: endFrame, scale: easeInScaleFromLinearScale(CGFloat(max(0.0, min(scale, 1.0)))))

Изменение коэффициента изменит эффект.Чем выше фактор, тем выше эффект.А использование коэффициента в нуле означало бы линейную кривую.

С этим вы можете сделать практически любой тип анимации.Единственное правило - ваша функция должна следовать f(0) = 0 и f(1) = 1.Это означает, что он начинается в начальной позиции и заканчивается в конечной позиции.

Хотя некоторые кривые немного сложнее.EaseInOut может показаться простым, но это не так.Мы, вероятно, хотели бы реализовать что-то вроде sin в диапазоне [-PI/2, PI/2].Затем сбалансируйте эту функцию с линейной функцией.Это одна из моих реализаций, которую я нашел:

return (sin((inputScale-0.5) * .pi)+1.0)/2.0 * (magnitude) + (inputScale * (1.0-magnitude))

Это поможет вам поиграть с каким-нибудь "онлайн-графическим калькулятором", чтобы найти ваши уравнения и затем преобразовать результаты в ваши функции.

...