iOS自定義轉場動畫

NO IMAGE

iOS自定義轉場動畫

本文記錄分享下自定義轉場動畫的實現方法,具體到動畫效果:新浪微博圖集瀏覽轉場效果、手勢過渡動畫、網易音樂啟動屏轉場動畫、開關門動畫、全屏側滑返回效果 的代碼可以到Github WSLTransferAnimation下載查看,註釋還算清晰。

模態化present和dismiss 自定義轉場

1、創建一個遵循協議的動畫過渡管理對象,並實現如下兩個方法:

//返回動畫事件
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext{
return 0.3;
}
//所有的過渡動畫事務都在這個方法裡面完成
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
//取出轉場前後的視圖控制器
UIViewController * fromVC = (UIViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController * toVC = (UIViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//取出轉場前後視圖控制器上的視圖view
UIView * toView = [transitionContext viewForKey:UITransitionContextToViewKey];
UIView * fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
//這裡有個重要的概念containerView,要做轉場動畫的視圖就必須要加入containerView上才能進行,可以理解containerView管理著所有做轉場動畫的視圖
UIView *containerView = [transitionContext containerView];
//如果加入了手勢交互轉場,就需要根據手勢交互動作是否完成/取消來做操作,完成標記YES,取消標記NO,必須標記,否則系統認為還處於動畫過程中,會出現無法交互之類的bug
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
if ([transitionContext transitionWasCancelled]) { 
//如果取消轉場
}else{
//完成轉場
}
}

2、自定義一個繼承於UIPercentDrivenInteractiveTransition的手勢過渡管理對象,可以根據手勢需要設置控制動畫轉場進度的百分比。

//必要調用實現的系統方法
//手勢過程中,通過updateInteractiveTransition設置轉場過程動畫進行的百分比,然後系統會根據百分比自動佈局動畫控件,不用我們控制了
[self updateInteractiveTransition:percentComplete];
//完成轉場操作
[self finishInteractiveTransition];
//取消轉場操作
[self cancelInteractiveTransition];

3、轉場時最上層的視圖控制器需要遵循的協議,並設置為代理,並實現如下代理方法:

//設置轉場代理
self.transitioningDelegate = self;
#pragma mark -- UIViewControllerTransitioningDelegate
//返回一個處理present動畫過渡的對象
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
return self.transitionAnimation;
}
//返回一個處理dismiss動畫過渡的對象
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
//這裡我們初始化dismissType
self.transitionAnimation.transitionType = WSLTransitionOneTypeDissmiss;
return self.transitionAnimation;
}
//返回一個處理present手勢過渡的對象 
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator{
return self.transitionInteractive;
}
//返回一個處理dismiss手勢過渡的對象
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator{
return self.transitionInteractive;
}

導航控制器push和pop 自定義轉場

1、略…同上
2、略… 同上
3、在push動畫之前設置導航控制器的轉場動畫代理,轉場時最上層的視圖控制器需要遵循的協議,並設置為代理,並實現如下代理方法:

 //在push動畫之前設置轉場動畫代理
self.navigationController.delegate = animationFour;
#pragma mark -- UINavigationControllerDelegate
//返回處理push/pop動畫過渡的對象
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC{
if (operation == UINavigationControllerOperationPush) {
self.transitionAnimation.transitionType = WSLTransitionTwoTypePush;
return self.transitionAnimation;
}else if (operation == UINavigationControllerOperationPop){
self.transitionAnimation.transitionType = WSLTransitionTwoTypePop;
}
return self.transitionAnimation;
}
//返回處理push/pop手勢過渡的對象 這個代理方法依賴於上方的方法 ,這個代理實際上是根據交互百分比來控制上方的動畫過程百分比
- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController{
//手勢開始的時候才需要傳入手勢過渡代理,如果直接pop或push,應該返回nil,否者無法正常完成pop/push動作
if ( self.transitionAnimation.transitionType == WSLTransitionTwoTypePop) {
return self.transitionInteractive.isInteractive == YES ? self.transitionInteractive : nil;
}
return nil;
}

全屏側滑返回

創建一個繼承於UINavigationController的一個對象WSLNavigatioController,遵守協議,實現如下方法:

  // 獲取系統自帶滑動手勢的target對象
id target = self.interactivePopGestureRecognizer.delegate;
// 創建全屏滑動手勢,調用系統自帶滑動手勢的target的action方法
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:@selector(handleNavigationTransition:)];
// 設置手勢代理,攔截手勢觸發
pan.delegate = self;
// 給導航控制器的view添加全屏滑動手勢
[self.view addGestureRecognizer:pan];
// 禁止使用系統自帶的滑動手勢
self.interactivePopGestureRecognizer.enabled = NO;
#pragma mark -- UIGestureRecognizerDelegate
// 什麼時候調用:每次觸發手勢之前都會詢問下代理,是否觸發。
// 作用:攔截手勢觸發
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
// 注意:只有非根控制器才有滑動返回功能,根控制器沒有。
// 判斷導航控制器是否只有一個子控制器,如果只有一個子控制器,肯定是根控制器
if (self.childViewControllers.count == 1) {
// 表示用戶在根控制器界面,就不需要觸發滑動手勢,
return NO;
}
return YES;
}
解決UIScrollView的滑動手勢與全屏側滑手勢的衝突

創建一個UIScrollView的類別UIScrollView+GestureConflict,重寫如下方法:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
// 首先判斷otherGestureRecognizer是不是系統pop手勢
if ([otherGestureRecognizer.view isKindOfClass:NSClassFromString(@"UILayoutContainerView")]) {
// 再判斷系統手勢的state是began還是fail,同時判斷scrollView的位置是不是正好在最左邊
if (otherGestureRecognizer.state == UIGestureRecognizerStateBegan && self.contentOffset.x == 0) {
return YES;
}
}
return NO;
}

更新於 2018/8/17 iOS 全屏側滑手勢/UIScrollView/UISlider間滑動手勢衝突

iOS自定義轉場動畫

相關文章

iOSUrlScheme實現APP間通信、分享

iOS瀑布流之柵格佈局

iOS瀑布流封裝

iOS仿系統指南針