本文是筆者學(xué)習(xí)iOS動畫的一些小總結(jié),接第二彈;
ViewController Transition Animation
之前在第一彈和第二彈中我們?yōu)槟硞€view或者layer的某個屬性做動畫, 移動,放縮,漸變等效果。此時我們利用之前的知識,可以通過UIKit提供的API來自定義VC之間的轉(zhuǎn)場動畫,例如Modal動畫,Push動畫,Pop動畫。
Modal 動畫
present(modalVC, animated: true, completion: nil)
我們經(jīng)常會用上述方式顯示一個控制器,默認的效果是從下往上的一個動畫,但是系統(tǒng)允許我們進行自定義這個呈現(xiàn)的效果,步驟如下:
- 遵守
UIViewControllerTransitioningDelegate協(xié)議
let modalVC = ModalViewController()
modalVC.transitioningDelegate = self
present(modalVC, animated: true, completion: nil)
- 實現(xiàn)兩個協(xié)議的方法
extension ViewController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return transition
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return transition
}
}
這里如果返回nil,那么UIKit還是會用系統(tǒng)默認的動畫效果進行呈現(xiàn)。
- 實現(xiàn)
UIViewControllerAnimatedTransitioning兩個方法
第二步中返回的對象是遵守UIViewControllerAnimatedTransitioning協(xié)議的對象;該協(xié)議有兩個必須實現(xiàn)的方法:
transitionDuration(using:): 告訴UIKit自定義的動畫的時間;
animateTransition(using:): 實現(xiàn)你想要的動畫效果;
下面給出一個漸變的Modal和Dismiss動畫栗子:
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return animationDuration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
let toView = transitionContext.view(forKey: .to)
containerView.addSubview(toView!)
toView?.alpha = 0.0
UIView.animate(withDuration: animationDuration, animations: {
toView?.alpha = 1.0
}, completion: { _ in
transitionContext.completeTransition(true)
})
}

transitionContext在實現(xiàn)動畫中非常重要,因為它提供了你需要做動畫的對象,以Modal為例,默認的控制器的view叫做fromView,modal出來的控制器的view叫做toView,fromView和toView都是放到containerView中顯示的,當(dāng)動畫結(jié)束后,fromView會從containerView中移除。
VCModalAnimationDemo只是簡單的一個效果,實際需要中,我們完全可以做出更加酷炫的轉(zhuǎn)場動畫。
Push/Pop 動畫
navigationController?.pushViewController(DetailViewController(), animated: true)
導(dǎo)航控制器進行Push操作的時候,默認的UIKit會提供一個從右往左的動畫,同樣的UIKit也允許我們進行自定義這個呈現(xiàn)效果,步驟如下:
- 遵守
UINavigationControllerDelegate協(xié)議
navigationController?.delegate = self
- 實現(xiàn)協(xié)議方法
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
transition.operation = operation
return transition
}
這里如果返回nil,那么UIKit還是會用系統(tǒng)默認的動畫效果進行呈現(xiàn)。
- 實現(xiàn) UIViewControllerAnimatedTransitioning兩個方法,這里其實和Modal動畫是一樣的,同樣的是協(xié)議中兩個必須要實現(xiàn)的方法。
下面給出一個Push時候進行放大圖形進行展示新控制器View的動畫,Pop時候進行放縮當(dāng)前View直到消失的動畫,效果如下圖:


完整的Demo下載地址VCPushPopAnimationDemo
ViewController Interactive Transition Animation
我們不但可以自定義控制器之間的轉(zhuǎn)場動畫,我們還可以通過添加手勢來控制轉(zhuǎn)場動畫,就像iOS7中新加的一個手勢返回控制器的效果,步驟如下:
- 實現(xiàn)
UINavigationControllerDelegate協(xié)議方法
func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return transition.transitionInProgress ? transition : nil
}
默認返回nil則不能控制動畫;
上述代碼中返回的transition對象是UIPercentDrivenInteractiveTransition的子類,該類遵守了UIViewControllerInteractiveTransitioning協(xié)議,使用該類我們不需要進行每一幀每一幀的控制動畫,只需要傳入根據(jù)手勢移動的位移即可,該類會自動顯示。
@objc func handlePanGesture(recognizer: UIPanGestureRecognizer) {
let translation = recognizer.translation(in: recognizer.view?.superview) // nivigation's view
var progress: CGFloat = abs(translation.x / 200.0)
progress = min(max(progress, 0.01), 0.99)
switch recognizer.state {
case .began:
transitionInProgress = true
navigationController.popViewController(animated: true)
case .changed:
update(progress)
case .cancelled, .ended:
if progress < 0.5 {
cancel()
} else {
finish()
}
transitionInProgress = false
default:
break
}
}
完整的Demo下載地址VCInteractiveNavigationAnimationDemo
最后
未完待續(xù)