iOS開發 tabbar自定義轉場動畫
阿新 • • 發佈:2018-11-21
1.小記
- 關於自定義轉場動畫,只要你理清他的”套路”,你就可以隨心所欲地自定義了.
- 大體思路就是:遵守對應的代理協議,然後設定對應的代理,實現代理方法,這個代理方法要返回的值就是你要實現的動畫.(如果返回nil,就是預設效果)
- 以UITabBarController為例的簡單轉場動畫demo地址 gitHub地址
2.基本介紹
在此介紹一下基本知識:
1.在哪裡寫我們自定義的動畫.
蘋果給我們提供了UIViewControllerAnimatedTransitioning協議,這個協議提供了我們需要的介面,遵守這個協議的物件實現動畫基本內容.
讓我們跳轉進去看看都有什麼:
@protocol UIViewControllerAnimatedTransitioning <NSObject> // This is used for percent driven interactive transitions, as well as for // container controllers that have companion animations that might need to // synchronize with the main animation. // 這個介面返回的值為動畫時長 - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext; // This method can only be a nop if the transition is interactive and not a percentDriven interactive transition. // 這個介面返回的值為具體動畫內容,也就是說,自定義的動畫操作都通過這個介面來實現 - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext; @optional /// A conforming object implements this method if the transition it creates can /// be interrupted. For example, it could return an instance of a /// UIViewPropertyAnimator. It is expected that this method will return the same /// instance for the life of a transition. - (id <UIViewImplicitlyAnimating>) interruptibleAnimatorForTransition:(id <UIViewControllerContextTransitioning>)transitionContext NS_AVAILABLE_IOS(10_0); // This is a convenience and if implemented will be invoked by the system when the transition context's completeTransition: method is invoked. - (void)animationEnded:(BOOL) transitionCompleted; @end
1.通過註釋的解釋,我們能夠知道,遵守UIViewControllerAnimatedTransitioning協議的物件就可以實現我們自定義的動畫.
2.通常我們會自定義NSObject的子類,遵守UIViewControllerAnimatedTransitioning協議,然後實現協議方法來自定義轉場動畫.
3.這個子類的物件就是我們的"自定義動畫".如果把自定義轉場動畫比作為做菜的話,那麼現在我們準備的就是食材.
- 在這裡要對一些概念進行下解釋,避免在自定義動畫時蒙圈
1.From和To 在自定義轉場動畫的程式碼中,經常會出現fromViewController和toViewController。如果錯誤的理解它們的含義會導致動畫邏輯完全錯誤。 fromViewController表示當前檢視容器,toViewController表示要跳轉到的檢視容器。如果是從A檢視控制器present到B,則A是from,B是to。從B檢視控制器dismiss到A時,B變成了from,A是to。 2.Presented和Presenting 這也是一組相對的概念,它容易與fromView和toView混淆。簡單來說,它不受present或dismiss的影響,如果是從A檢視控制器present到B,那麼A總是B的presentingViewController, B總是A的presentedViewController。
2.在哪裡用我們自定義的動畫.
這裡要介紹三個協議: 注意每個協議方法的返回值,都是遵守UIViewControllerAnimatedTransitioning的物件
1.協議一: UIViewControllerTransitioningDelegate
// 實現present/dismiss動畫的介面.
// 令我們需要自定義動畫的控制器遵守UIViewControllerTransitioningDelegate協議,並設定代理,實現協議方法,返回遵守UIViewControllerAnimatedTransitioning協議的類物件即可
// 在這裡需要清楚一點,假設由控制器A present 到B, A遵守UIViewControllerTransitioningDelegate協議,則設定B.transitioningDelegate = A,並設定B.modalPresentationStyle = UIModalPresentationCustom(或UIModalPresentationFullScreen);
// 一定要設定modalPresentationStyle,不然還是預設的轉場動畫.
// present動畫
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
// dismiss動畫
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
# modalPresentationStyl
// 這是一個列舉型別,表示present時動畫的型別。
// 其中可以自定義動畫效果的只有兩種:FullScreen和Custom,兩者的區別在於FullScreen會移除fromView,而Custom不會。
2.協議二:UINavigationControllerDelegate
// 實現push/pop動畫的介面
// 這裡同樣是要遵守協議,設定代理,實現協議方法.
// 注意這裡設定的是navigationController.delegate, self.navigationController.delegate = self.
// 我在其他的部落格中看到: (注意: 這裡的 self.navigationController.delegate = self 最好寫在當前控制器的viewDidAppear方法中, 不然會導致在此push時無動畫效果),為什麼會失效我還不清楚,希望讀者能夠找到並分享一下~
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
3.協議三:UITabBarControllerDelegate
// 實現tabBarController切換子控制器的動畫
// 還是老套路,遵守協議,設定代理,實現協議方法
// 只是這裡要設定tabBarController的代理,我的做法就是在UITabBarController的viewDidLoad方法裡設定代理: self.delegate = self;
- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
animationControllerForTransitionFromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
- 小結: 從上面三個協議的返回值能夠看出,返回的東西就是我們2.1自定義遵守UIViewControllerAnimatedTransitioning協議的類的物件.
3.轉場動畫的思路(純個人理解,起個拋磚引玉作用~)
- 步驟一: 明確做哪種轉場動畫(做哪種菜,是魯菜,還是川菜?)
自定義present/dismiss動畫要遵守UIViewControllerTransitioningDelegate協議
自定義push/pop動畫要遵守UINavigationControllerDelegate協議
自定義tabbarController轉場動畫要遵守UITabBarControllerDelegate協議
以demo為例:
// DMMainViewController.m檔案
@interface DMMainViewController ()<UITabBarControllerDelegate>// 遵守協議
@end
@implementation DMMainViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.delegate = self;// 設定代理
UIViewController *vc = [[UIViewController alloc] init];
vc.view.backgroundColor = [UIColor whiteColor];
[self setChildchildViewController:vc index:0 title:@"我是A"];
[self setChildchildViewController:[[UITableViewController alloc] init] index:1 title:@"我是B"];
[self setChildchildViewController:[[UIViewController alloc] init] index:2 title:@"我是C"];
}
// 動畫 實現協議方法
- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
return [[AnimationManager alloc] init];
}
這裡其實就是遵守協議,設定代理,實現協議方法.
- 步驟二: 確定做哪樣轉場動畫(如果選擇了魯菜,是德州扒雞,還是紅燒大蝦?如果選擇了川菜,是四川火鍋,還是水煮魚?)
// AnimationManager.h檔案
// 自定義NSObject的子類,遵守UIViewControllerAnimatedTransitioning協議
@interface AnimationManager : NSObject<UIViewControllerAnimatedTransitioning>
@property (nonatomic, assign) KAnimationType type;
- (instancetype)initWithType:(KAnimationType)type;
@end
// AnimationManager.m檔案
#import "AnimationManager.h"
#import "DMNavigationViewController.h"
@interface AnimationManager ()
@end
@implementation AnimationManager
// 這個是動畫時長
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return 0.5;
}
// 具體動畫,在這裡可以根據你的想象去實現你要的動畫效果了
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
// 獲取fromVc和toVc
DMNavigationViewController *fromVc = (DMNavigationViewController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
DMNavigationViewController *toVc = (DMNavigationViewController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *fromV = fromVc.view;
UIView *toV = toVc.view;
// 轉場環境
UIView *containView = [transitionContext containerView];
containView.backgroundColor = [UIColor whiteColor];
// 判斷滑動方向
if (toVc.index > fromVc.index) {
toV.frame = CGRectMake([UIScreen mainScreen].bounds.size.width, 0, containView.frame.size.width, containView.frame.size.height);
[containView addSubview:toV];
// 動畫
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromV.transform = CGAffineTransformTranslate(fromV.transform, -[UIScreen mainScreen].bounds.size.width,0);// containView.frame.size.height
toV.transform = CGAffineTransformTranslate(toV.transform, -[UIScreen mainScreen].bounds.size.width, 0);
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
}else if (toVc.index < fromVc.index) {
toV.frame = CGRectMake(- [UIScreen mainScreen].bounds.size.width, 0, containView.frame.size.width, containView.frame.size.height);
[containView addSubview:toV];
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromV.transform = CGAffineTransformTranslate(fromV.transform, [UIScreen mainScreen].bounds.size.width,0);
toV.transform = CGAffineTransformTranslate(toV.transform, [UIScreen mainScreen].bounds.size.width, 0);
} completion:^(BOOL finished) {
[fromV removeFromSuperview];
[transitionContext completeTransition:YES];
}];
}
}
@end
# 這裡面就涉及到前面講的 1.From和To的關係,2.Presented和Presenting的關係.在2.1的底部有介紹
小結:
所謂的自定義轉場動畫,就是把系統預設的換成我們自己寫的而已,關鍵就是在這些協議裡.理清控制器與協議的關係.
簡單的畫了一個結構圖
附: 以UITabBarController為例的簡單轉場動畫demo地址 gitHub地址