您好,登錄后才能下訂單哦!
前言
最近有些空閑時間,整理了下最近做的項目,本文主要介紹了關于iOS自定義控制器轉場動畫push的相關內容,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧。
效果圖:
iOS7 開始蘋果推出了自定義轉場的 API 。從此,任何可以用 CoreAnimation 實現的動畫,都可以出現在兩個 ViewController 的切換之間。并且實現方式高度解耦,這也意味著在保證代碼干凈的同時想要替換其他動畫方案時只需簡單改一個類名就可以了,真正體會了一把高顏值代碼帶來的愉悅感。
其實網上關于自定義轉場動畫的教程很多,這里我是希望同學們能易懂,易上手。
轉場分兩種Push和Modal,所以自定義轉場動畫也就肯定分兩種,今天我們講的是Push
自定義轉場動畫Push
首先搭建界面,添加4個按鈕:
- (void)addButton{ self.buttonArr = [NSMutableArray array]; CGFloat margin=50; CGFloat width=(self.view.frame.size.width-margin*3)/2; CGFloat height = width; CGFloat x = 0; CGFloat y = 0; //列 NSInteger col = 2; for (NSInteger i = 0; i < 4; i ++) { x = margin + (i%col)*(margin+width); y = margin + (i/col)*(margin+height) + 150; UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; button.frame = CGRectMake(x, y, width, height); button.layer.cornerRadius = width * 0.5; [button addTarget:self action:@selector(btnclick:) forControlEvents:UIControlEventTouchUpInside]; button.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1.0]; button.tag = i+1; [self.view addSubview:button]; [self.buttonArr addObject:button]; } }
添加動畫:
- (void)setupButtonAnimation{ [self.buttonArr enumerateObjectsUsingBlock:^(UIButton * _Nonnull button, NSUInteger idx, BOOL * _Nonnull stop) { // positionAnimation CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; positionAnimation.calculationMode = kCAAnimationPaced; positionAnimation.fillMode = kCAFillModeForwards; positionAnimation.repeatCount = MAXFLOAT; positionAnimation.autoreverses = YES; positionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; positionAnimation.duration = (idx == self.buttonArr.count - 1) ? 4 : 5+idx; UIBezierPath *positionPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(button.frame, button.frame.size.width/2-5, button.frame.size.height/2-5)]; positionAnimation.path = positionPath.CGPath; [button.layer addAnimation:positionAnimation forKey:nil]; // scaleXAniamtion CAKeyframeAnimation *scaleXAniamtion = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.x"]; scaleXAniamtion.values = @[@1.0,@1.1,@1.0]; scaleXAniamtion.keyTimes = @[@0.0,@0.5,@1.0]; scaleXAniamtion.repeatCount = MAXFLOAT; scaleXAniamtion.autoreverses = YES; scaleXAniamtion.duration = 4+idx; [button.layer addAnimation:scaleXAniamtion forKey:nil]; // scaleYAniamtion CAKeyframeAnimation *scaleYAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.y"]; scaleYAnimation.values = @[@1,@1.1,@1.0]; scaleYAnimation.keyTimes = @[@0.0,@0.5,@1.0]; scaleYAnimation.autoreverses = YES; scaleYAnimation.repeatCount = YES; scaleYAnimation.duration = 4+idx; [button.layer addAnimation:scaleYAnimation forKey:nil]; }]; }
界面搭建好了:
然后想在Push的時候實現自定義轉場動畫首先要遵守一個協議UINavigationControllerDelegate
蘋果在 UINavigationControllerDelegate 中給出了幾個協議方法,通過返回類型就可以很清楚地知道各自的具體作用。
//用來自定義轉場動畫 - (nullable id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
//為這個動畫添加用戶交互 - (nullable id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);
在第一個方法里只要返回一個準守UIViewControllerInteractiveTransitioning協議的對象,并在里面實現動畫即可
//返回動畫時間 - (NSTimeInterval)transitionDuration:(nullable id)transitionContext; //將動畫的代碼寫到里面即可 - (void)animateTransition:(id)transitionContext;
首先我自定義一個名為LRTransitionPushController的類繼承于NSObject準守了UIViewControllerAnimatedTransitioning協議
- (void)animateTransition:(id)transitionContext{ self.transitionContext = transitionContext; //獲取源控制器 注意不要寫成 UITransitionContextFromViewKey LRTransitionPushController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; //獲取目標控制器 注意不要寫成 UITransitionContextToViewKey LRTransitionPopController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; //獲得容器視圖 UIView *containView = [transitionContext containerView]; // 都添加到container中。注意順序 目標控制器的view需要后面添加 [containView addSubview:fromVc.view]; [containView addSubview:toVc.view]; UIButton *button = fromVc.button; //繪制圓形 UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:button.frame]; //創建兩個圓形的 UIBezierPath 實例;一個是 button 的 size ,另外一個則擁有足夠覆蓋屏幕的半徑。最終的動畫則是在這兩個貝塞爾路徑之間進行的 //按鈕中心離屏幕最遠的那個角的點 CGPoint finalPoint; //判斷觸發點在那個象限 if(button.frame.origin.x > (toVc.view.bounds.size.width / 2)){ if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) { //第一象限 finalPoint = CGPointMake(0, CGRectGetMaxY(toVc.view.frame)); }else{ //第四象限 finalPoint = CGPointMake(0, 0); } }else{ if (button.frame.origin.y < (toVc.view.bounds.size.height / 2)) { //第二象限 finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), CGRectGetMaxY(toVc.view.frame)); }else{ //第三象限 finalPoint = CGPointMake(CGRectGetMaxX(toVc.view.frame), 0); } } CGPoint startPoint = CGPointMake(button.center.x, button.center.y); //計算向外擴散的半徑 = 按鈕中心離屏幕最遠的那個角距離 - 按鈕半徑 CGFloat radius = sqrt((finalPoint.x-startPoint.x) * (finalPoint.x-startPoint.x) + (finalPoint.y-startPoint.y) * (finalPoint.y-startPoint.y)) - sqrt(button.frame.size.width/2 * button.frame.size.width/2 + button.frame.size.height/2 * button.frame.size.height/2); UIBezierPath *endPath = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(button.frame, -radius, -radius)]; //賦值給toVc視圖layer的mask CAShapeLayer *maskLayer = [CAShapeLayer layer]; maskLayer.path = endPath.CGPath; toVc.view.layer.mask = maskLayer; CABasicAnimation *maskAnimation =[CABasicAnimation animationWithKeyPath:@"path"]; maskAnimation.fromValue = (__bridge id)startPath.CGPath; maskAnimation.toValue = (__bridge id)endPath.CGPath; maskAnimation.duration = [self transitionDuration:transitionContext]; maskAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; maskAnimation.delegate = self; [maskLayer addAnimation:maskAnimation forKey:@"path"]; }
在控制器里面用來自定義轉場動畫的方法里返回剛才自定義的動畫類
- (id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{ if (operation == UINavigationControllerOperationPush) { return [LRTranstionAnimationPush new]; }else{ return nil; } }
到此為止自定義轉場動畫就完成了
pop的動畫只是把push動畫反過來做一遍這里就不細講了,有疑問的可以去看代碼
添加滑動返回手勢
上面說到這個方法是為這個動畫添加用戶交互的所以我們要在pop時實現滑動返回
最簡單的方式應該就是利用UIKit提供的UIPercentDrivenInteractiveTransition類了,這個類已經實現了UIViewControllerInteractiveTransitioning協議,同學men可以通過這個類的對象指定轉場動畫的完成百分比。
//為這個動畫添加用戶交互 - (nullable id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id) animationController NS_AVAILABLE_IOS(7_0);
第一步 添加手勢
UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; [self.view addGestureRecognizer:gestureRecognizer];
第二步 通過用戶滑動的變化確定動畫執行的比例
- (void)handlePan:(UIPanGestureRecognizer *)gestureRecognizer { /*調用UIPercentDrivenInteractiveTransition的updateInteractiveTransition:方法可以控制轉場動畫進行到哪了, 當用戶的下拉手勢完成時,調用finishInteractiveTransition或者cancelInteractiveTransition,UIKit會自動執行剩下的一半動畫, 或者讓動畫回到最開始的狀態。*/ if ([gestureRecognizer translationInView:self.view].x>=0) { //手勢滑動的比例 CGFloat per = [gestureRecognizer translationInView:self.view].x / (self.view.bounds.size.width); per = MIN(1.0,(MAX(0.0, per))); if (gestureRecognizer.state == UIGestureRecognizerStateBegan) { self.interactiveTransition = [UIPercentDrivenInteractiveTransition new]; [self.navigationController popViewControllerAnimated:YES]; } else if (gestureRecognizer.state == UIGestureRecognizerStateChanged){ if([gestureRecognizer translationInView:self.view].x ==0){ [self.interactiveTransition updateInteractiveTransition:0.01]; }else{ [self.interactiveTransition updateInteractiveTransition:per]; } } else if (gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled){ if([gestureRecognizer translationInView:self.view].x == 0){ [self.interactiveTransition cancelInteractiveTransition]; self.interactiveTransition = nil; }else if (per > 0.5) { [ self.interactiveTransition finishInteractiveTransition]; }else{ [ self.interactiveTransition cancelInteractiveTransition]; } self.interactiveTransition = nil; } } else if (gestureRecognizer.state == UIGestureRecognizerStateChanged){ [self.interactiveTransition updateInteractiveTransition:0.01]; [self.interactiveTransition cancelInteractiveTransition]; } else if ((gestureRecognizer.state == UIGestureRecognizerStateEnded || gestureRecognizer.state == UIGestureRecognizerStateCancelled)){ self.interactiveTransition = nil; } }
第三步 在為動畫添加用戶交互的代理方法里返回UIPercentDrivenInteractiveTransition的實例
- (id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id) animationController { return self.interactiveTransition; }
如果感覺這篇文章對您有所幫助,順手點個喜歡,謝謝啦
代碼放在了GitHub上大家可以下載,當然也可以通過本地下載
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。