前言
系統(tǒng)自帶的push和pop動(dòng)畫(huà)已經(jīng)能滿足絕大部份使用場(chǎng)景了。騰訊新聞、汽車之家等App的push和pop動(dòng)畫(huà)和系統(tǒng)的不一樣,是自定義的,找了點(diǎn)空閑時(shí)間,研究了哈自定義控制器轉(zhuǎn)場(chǎng)動(dòng)畫(huà)。

QQ20170515-153720.gif
1、聲明一個(gè)枚舉,自定義一個(gè)指定構(gòu)造器方法,根據(jù)枚舉值來(lái)確定該使用push或者pop動(dòng)畫(huà)
enum TransitionAniamtionsType {
case push,pop
}
var transitionAniamtionsType: TransitionAniamtionsType = .push
public init(type: TransitionAniamtionsType) {
transitionAniamtionsType = type
}
2、新建一個(gè)類,遵守<UIViewControllerAnimatedTransitioning>協(xié)議,實(shí)現(xiàn)兩個(gè)必需實(shí)現(xiàn)的方法
//返回轉(zhuǎn)場(chǎng)動(dòng)畫(huà)持續(xù)時(shí)間
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
//轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的具體實(shí)現(xiàn)
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
if transitionAniamtionsType == .push {
pushAnimation(using: transitionContext)
}else if transitionAniamtionsType == .pop {
popAnimation(using: transitionContext)
}
}
3、具體動(dòng)畫(huà)邏輯,本人實(shí)現(xiàn)的動(dòng)畫(huà)比較簡(jiǎn)單,喜歡研究的大神可以根據(jù)需要實(shí)現(xiàn)更復(fù)雜的動(dòng)畫(huà)。具體動(dòng)畫(huà)實(shí)現(xiàn)代碼如下
//push
fileprivate func pushAnimation(using transitionContext: UIViewControllerContextTransitioning) {
//獲取fromVc和toVc的view
guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from),
let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else { return }
let containerView = transitionContext.containerView
//設(shè)置toVc的view的frame,fromVc就是當(dāng)前界面上的vc就不需要設(shè)置和添加了
toView.frame = CGRect(x: screen.width, y: 64, width: screen.width, height: screen.height - 64)
containerView.addSubview(toView)
//執(zhí)行動(dòng)畫(huà)
UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: {
fromView.alpha = 0.8
fromView.transform = CGAffineTransform.init(scaleX: 0.9, y: 0.9)
toView.frame = CGRect(x: 0, y: 64, width: self.screen.width, height: self.screen.height - 64)
}) { (finish) in
//!transitionContext.transitionWasCancelled 轉(zhuǎn)場(chǎng)動(dòng)畫(huà)是否被取消(手勢(shì)交互需要這樣寫,如果不需要手勢(shì)的話直接寫成true就可以了)
//transitionContext.completeTransition這個(gè)方法在動(dòng)畫(huà)執(zhí)行完后必需調(diào)用
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
//pop
fileprivate func popAnimation(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from),
let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else { return }
let containerView = transitionContext.containerView
toView.transform = CGAffineTransform.init(scaleX: 0.9, y: 0.9)
toView.alpha = 0.8
containerView.insertSubview(toView, at: 0)
UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: {
toView.alpha = 1
toView.transform = CGAffineTransform.identity
fromView.frame = CGRect(x: self.screen.width, y: 64, width: self.screen.width, height: self.screen.height - 64)
}) { (finish) in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
4、在需要?jiǎng)赢?huà)的控制中設(shè)置代理,實(shí)現(xiàn)代理方法
\\每次顯示這個(gè)控制器的view的時(shí)候都重新設(shè)置代理,不這樣的做的話pop回來(lái),下次在push的時(shí)候就不會(huì)去執(zhí)行返回動(dòng)畫(huà)的代理了
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.navigationController?.delegate = self
}
extension ViewController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if operation == .push {
return TransitionAnimations.init(type: .push)
}else if operation == .pop {
return TransitionAnimations.init(type: .pop)
}else {
return nil
}
}
}
5、手勢(shì)交互
手勢(shì)交互實(shí)現(xiàn)起來(lái)還是比較方便的,蘋果已經(jīng)為我們封裝好了相關(guān)的方法了,我們只需要添加一個(gè)手勢(shì)來(lái)更新相關(guān)參數(shù)就可以了
//在viewDidLoad中實(shí)現(xiàn)
//聲明一個(gè)交互對(duì)象
var interactiveTransition: UIPercentDrivenInteractiveTransition?
func initializeInterface() {
self.navigationController?.delegate = self
view.backgroundColor = UIColor.yellow
let panGesture: UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(respondsToPanGesture))
view.addGestureRecognizer(panGesture)
}
fileprivate func respondsToPanGesture(pan: UIPanGestureRecognizer) {
let moveX = pan.translation(in: pan.view).x
let percent = moveX / screen.width
switch pan.state {
case .began:
interactiveTransition = UIPercentDrivenInteractiveTransition()
navigationController?.popViewController(animated: true)
case .changed:
//根據(jù)百分比更新交互
interactiveTransition?.update(percent)
case .ended:
if percent > 0.5 {
interactiveTransition?.finish()
}else {
interactiveTransition?.cancel()
}
interactiveTransition = nil
default:
print("default")
}
}
最后在該控制器中實(shí)現(xiàn)navigationdelegate的方法
extension BViewController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if operation == .push {
return TransitionAnimations.init(type: .push)
}else if operation == .pop {
return TransitionAnimations.init(type: .pop)
}else {
return nil
}
}
func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
// 返回交互對(duì)象
return interactiveTransition
}
}
Demo 地址