交互式 UIView 翻转过渡
Interactive UIView flip transition
我有一张卡片的正面和背面。我像这样为两者之间的过渡设置动画:
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
,或者是否有其他替代方法可以使翻转互动?
最后我自己写了。代码很长,所以这里是 GitHub 上的 link 到 the full program。以下是关键部分:
所有内容都封装在一个 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 link。
我有一张卡片的正面和背面。我像这样为两者之间的过渡设置动画:
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
,或者是否有其他替代方法可以使翻转互动?
最后我自己写了。代码很长,所以这里是 GitHub 上的 link 到 the full program。以下是关键部分:
所有内容都封装在一个 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 link。