Интерактивный флип-переход UIView - PullRequest
0 голосов
/ 27 октября 2018

У меня есть передняя и задняя часть карты. Я оживляю переход между ними так:

private func flipToBack() {
    UIView.transition(from: frontContainer, to: backContainer, duration: 0.5, options: [.transitionFlipFromRight, .showHideTransitionViews], completion: nil)
}

private func flipToFront() {
    UIView.transition(from: backContainer, to: frontContainer, duration: 0.5, options: [.transitionFlipFromLeft, .showHideTransitionViews], completion: nil)
}

Это работает отлично. Однако я хочу сделать эту анимацию интерактивной, чтобы при горизонтальном перемещении пользователя по всей карте анимация переворота развивалась пропорционально. Обычно я делаю этот вид интерактивной анимации с UIViewPropertyAnimator, но я не знаю, какое свойство я бы анимировал в аниматоре, не создавая анимацию с нуля.

Можно ли использовать UIViewPropertyAnimator или есть какая-то другая альтернатива, чтобы сделать флип интерактивным?

1 Ответ

0 голосов
/ 29 октября 2018

Я закончил писать сам.Код довольно длинный, поэтому вот ссылка на полную программу на GitHub.Вот ключевые части:

Все заключено в объект InteractiveFlipAnimator, который имеет вид спереди (v1) и вид сзади (v2).Каждое представление также получает черную обложку, которая выполняет функцию тени, чтобы добавить эффект затемнения, когда представление поворачивается в перспективе.

Вот функция панорамирования:

/// Add a `UIPanGestureRecognizer` to the main view that contains the card and pass it onto this function.
@objc func pan(_ gesture: UIPanGestureRecognizer) {
    guard let view = gesture.view else { return }
    if isAnimating { return }

    let translation = gesture.translation(in: view)
    let x = translation.x
    let angle = startAngle + CGFloat.pi * x / view.frame.width

    // If the angle is outside [-pi, 0], then do not rotate the view and count it as touchesEnded. This works because the full width is the screen width.
    if angle < -CGFloat.pi || angle > 0 {
        if gesture.state != .began && gesture.state != .changed {
            finishedPanning(angle: angle, velocity: gesture.velocity(in: view))
        }
        return
    }

    var transform = CATransform3DIdentity
    // Perspective transform
    transform.m34 = 1 / -500
    // y rotation transform
    transform = CATransform3DRotate(transform, angle, 0, 1, 0)
    self.v1.layer.transform = transform
    self.v2.layer.transform = transform

    // Set the shadow
    if startAngle == 0 {
        self.v1Cover.alpha = 1 - abs(x / view.frame.width)
        self.v2Cover.alpha = abs(x / view.frame.width)
    } else {
        self.v1Cover.alpha = abs(x / view.frame.width)
        self.v2Cover.alpha = 1 - abs(x / view.frame.width)
    }

    // Set which view is on top. This flip happens when it looks like the two views make a vertical line.
    if abs(angle) < CGFloat.pi / 2 {
        // Flipping point
        v1.layer.zPosition = 0
        v2.layer.zPosition = 1
    } else {
        v1.layer.zPosition = 1
        v2.layer.zPosition = 0
    }

    // Save state
    if gesture.state != .began && gesture.state != .changed {
        finishedPanning(angle: angle, velocity: gesture.velocity(in: view))
    }
}

Код для завершения панорамирования очень похож, но он также намного длиннее.Чтобы увидеть все это вместе, посетите ссылку GitHub выше.

...