我在问题中发现,
自iOS 7以来推出的推送/弹出动画转换
UINavigationControllerDelegate
,更准确地说是方法:
navigationController(_:animationControllerFor:from:to:)
从根本上复制UINavigationController的动画转换。
这允许我添加一个
mask view
到在推送操作期间转换离开的视图,以便剪裁参考底图部分,并且不会出现可见的瑕疵。
推
操作(最终,我也要做pop操作,还有基于pop的交互式切换形式swipe手势),
我做了个习惯
UINavigationController
子类
,和
put it on GitHub
.
可归结为以下步骤:
-
首先,转换
.to
右侧视图,屏幕外。在动画中,逐渐将其变换回标识(屏幕中心)。
-
设置
作为
mask
到
.from
视图,初始边界等于
视图(无遮罩)。在动画中,逐渐减小遮罩的宽度
frame
属性设置为其初始值的一半。
-
在动画中,逐渐平移
from
class AnimationController: NSObject, UIViewControllerAnimatedTransitioning {
// Super slow for debugging:
let duration: TimeInterval = 1
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromView = transitionContext.view(forKey: .from) else {
return
}
guard let toView = transitionContext.view(forKey: .to) else {
return
}
guard let toViewController = transitionContext.viewController(forKey: .to) else {
return
}
//
// (Code below assumes navigation PUSH operation. For POP,
// use similar code but with view roles and direction
// reversed)
//
// Add target view to container:
transitionContext.containerView.addSubview(toView)
// Set tagret view frame, centered on screen
let toViewFinalFrame = transitionContext.finalFrame(for: toViewController)
toView.frame = toViewFinalFrame
let containerBounds = transitionContext.containerView.bounds
toView.center = CGPoint(x: containerBounds.midX, y: containerBounds.midY)
// But translate it to fully the RIGHT, for now:
toView.transform = CGAffineTransform(translationX: containerBounds.width, y: 0)
// We will slide the source view out, to the LEFT, by half as much:
let fromTransform = CGAffineTransform(translationX: -0.5*containerBounds.width, y: 0)
// Apply a white UIView as mask to the source view:
let maskView = UIView(frame: CGRect(origin: .zero, size: fromView.frame.size))
maskView.backgroundColor = .white
fromView.mask = maskView
// The mask will shrink to half the width during the animation:
let maskViewNewFrame = CGRect(origin: .zero, size: CGSize(width: 0.5*fromView.frame.width, height: fromView.frame.height))
// Animate:
UIView.animate(withDuration: duration, delay: 0, options: [.curveEaseOut], animations: {
fromView.transform = fromTransform // off-screen to the left (half)
toView.transform = .identity // Back on screen, fully centered
maskView.frame = maskViewNewFrame // Mask to half width
}, completion: {(completed) in
// Remove mask, or funny things will happen to a
// table view or scroll view if the user
// "rubber-bands":
fromView.mask = nil
transitionContext.completeTransition(completed)
})
}
(为了清晰起见,我在局部视图控制器中添加了一个文本视图)