首先自定义转场得遵循UINavigationControllerDelegate(push、pop转场)、UIViewControllerAnimatedTransitioning(present、dismiss转场).对应到属性就是UIViewController的
transitioningDelegate
与UINavigationController的delegate
.与UITabbarController的delegate
.
目前支持的就是UIViewController的presentViewController与dismissViewControllerXXX,与UINaivgationController的pushViewController与pop,与UITbbarController的selectIndex.(这个可以仿安卓的切换Tab然后VC左右转动切换的效果.详情可以看下面的资源).
UIViewControllerAnimatedTransitioning
代理
UIViewControllerAnimatedTransitioning
代理主要是两个必须实现的代理方法1.转场时间.2.转场动画.(注释说的也很清楚,仅仅是非用户交互的那么就是空,如果是百分比驱动的(这个后面会提到)就不会是空).
UIViewControllerTransitioningDelegate
代理
UIViewControllerTransitioningDelegate
代理方法主要是指定UIViewController动画方法的执行对象.对于不带用户交互的转场我们可以只看下面的三个方法.
- (nullable id )animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
- (nullable id )animationControllerForDismissedController:(UIViewController *)dismissed;
- (nullable UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(nullable UIViewController *)presenting sourceViewController:(UIViewController *)source NS_AVAILABLE_IOS(8_0);
第三个方法一般是用来做自定义弹窗的.我们需要区分是present还是dismiss只需要上面俩方法就行.上面俩代理方法用来指定做动画的对象.我在下面的资源里面直接指定的self.也就是动画管理类来控制.
使用注意:
/// 1. 不进行如下设置可能动画会出现问题
fromVc.modalPresentationStyle = UIModalPresentationCustom;
// Called when the navigation controller shows a new top view controller via a push, pop or setting of the view controller stack.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
- (UIInterfaceOrientationMask)navigationControllerSupportedInterfaceOrientations:(UINavigationController *)navigationController NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED;
- (UIInterfaceOrientation)navigationControllerPreferredInterfaceOrientationForPresentation:(UINavigationController *)navigationController NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED;
- (nullable id )navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id ) animationController NS_AVAILABLE_IOS(7_0);
- (nullable id )navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
和interactive有关的我们可以暂时不管.我们这次主要需要关注的是最后一个方法
- (nullable id )navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
我们可以在里面拿到fromvc和tovc.在operation中我们可以得到是push还是pop
typedef NS_ENUM(NSInteger, UINavigationControllerOperation) {
UINavigationControllerOperationNone,
UINavigationControllerOperationPush,
UINavigationControllerOperationPop,
};
所以,转场动画到此,我们可以把Nav和VC的转场动画放到同一个方法内处理.下面资源内的2.
就是这么处理的
UITabbarController的转场动画和VC与Nav不太一样.虽说能归类成当前显示的是from.显示的是to.然而根据一些产品的需求.咱们得判断一下当前的selectIndex是比点击后的Index大还是小…UITbbarController的转场就是大家点击tabbar的时候切换Index的
转场
.
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController NS_AVAILABLE_IOS(3_0);
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController;
- (void)tabBarController:(UITabBarController *)tabBarController willBeginCustomizingViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers NS_AVAILABLE_IOS(3_0) __TVOS_PROHIBITED;
- (void)tabBarController:(UITabBarController *)tabBarController willEndCustomizingViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers changed:(BOOL)changed NS_AVAILABLE_IOS(3_0) __TVOS_PROHIBITED;
- (void)tabBarController:(UITabBarController *)tabBarController didEndCustomizingViewControllers:(NSArray<__kindof UIViewController *> *)viewControllers changed:(BOOL)changed __TVOS_PROHIBITED;
- (UIInterfaceOrientationMask)tabBarControllerSupportedInterfaceOrientations:(UITabBarController *)tabBarController NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED;
- (UIInterfaceOrientation)tabBarControllerPreferredInterfaceOrientationForPresentation:(UITabBarController *)tabBarController NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED;
- (nullable id )tabBarController:(UITabBarController *)tabBarController
interactionControllerForAnimationController: (id )animationController NS_AVAILABLE_IOS(7_0);
- (nullable id )tabBarController:(UITabBarController *)tabBarController
animationControllerForTransitionFromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
一般来说.不做转场的交互的话我们只需要关注:
- (nullable id )tabBarController:(UITabBarController *)tabBarController
animationControllerForTransitionFromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
我们可以根据fromVC和toVC获取到Index的大小.然后看以什么样的方式完成动画.
到此为止,我们介绍了在不使用用户交互的情况下什么转场应该遵循什么样的代理.下面介绍一下动画中的一些概念.
- (void)animateTransition:(id)transitionContext;
既然这个动画方法只暴露了一个transitionContext
.那么和动画有关的东西应该都在这个上下文中.
containerView
.// The view in which the animated transition should take place.
#if UIKIT_DEFINE_AS_PROPERTIES
@property(nonatomic, readonly) UIView *containerView;
#else
- (UIView *)containerView;
#endif
我们要做动画的话需要把VC的View放到containerView
上操作.
可以通过- (nullable __kindof UIViewController *)viewControllerForKey:(UITransitionContextViewControllerKey)key;
方法取到对应的VC
UIKIT_EXTERN UITransitionContextViewControllerKey const UITransitionContextFromViewControllerKey NS_AVAILABLE_IOS(7_0);
UIKIT_EXTERN UITransitionContextViewControllerKey const UITransitionContextToViewControllerKey NS_AVAILABLE_IOS(7_0);
或者是通过- (nullable __kindof UIView *)viewForKey:(UITransitionContextViewKey)key NS_AVAILABLE_IOS(8_0);
取到对应的View
UIKIT_EXTERN UITransitionContextViewKey const UITransitionContextFromViewKey NS_SWIFT_NAME(from) NS_AVAILABLE_IOS(8_0);
UIKIT_EXTERN UITransitionContextViewKey const UITransitionContextToViewKey NS_SWIFT_NAME(to) NS_AVAILABLE_IOS(8_0);
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
// 转场失败后的处理
if ([transitionContext transitionWasCancelled]) {
}
这个completeTransition:
需要显示的调用.不然有时候系统可能认为你动画没做完.就卡住了…
资源.如果以后封装得更加易用一些我会考虑放到GitHub开源