UIViewControllerAnimatedTransitioning是iOS系統提供的轉場動畫協議,遵循該協議可自定義轉場動畫。
系統模態一個控制器時提供了模態風格的屬性
分別是從底部滑入,水平翻轉進入,交叉溶解以及翻頁這四種風格,不受iPhone和iPad限制。
接手了一個需求是以push動畫present一個控制器,系統提供的風格并不滿足要求,只能動手改造轉場動畫。
忽略我的丑字。嘿。
過程:
1.控制A present控制器B
2.生成遵循UIViewControllerTransitioningDelegate協議的自定義TransitioningDelegate,將 A.transitioningDelegate = 自定義transitioningdelegate
3.自定義transitioningdelegate生成自定義animatedTransitioning
4.自定義animatedTransitioning遵循UIViewControllerAnimatedTransitioning協議,實現兩個必要方法動畫時長及核心動畫。
話不多說,上代碼
1.自定義轉場動畫代理
自定義transitioningdelegate.h 暴露了transitioningdelegate生成自定義animatedTransitioning的方法, targetEdge屬性用來指定轉場動畫方向,可選UIRectEdgeTop,UIRectEdgeBottom,UIRectEdgeLeft,UIRectEdgeRight。用present實現push效果時使用UIRectEdgeRight。
使用自定義transitioningdelegate生成遵循轉場動畫協議的自定義類
2.實現轉場動畫核心動畫方法
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
//源控制器
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
//目標控制器
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// Present:
// fromView = The presenting view.
// toView = The presented view.
// Dismiss:
// fromView = The presented view.
// toView = The presenting view.
//容器視圖
UIView *containerView = transitionContext.containerView;
UIView *fromView = nil;
UIView *toView = nil;
if ([transitionContext respondsToSelector:@selector(viewForKey:)])
{
//iOS8之后
fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
toView = [transitionContext viewForKey:UITransitionContextToViewKey];
}
else
{
//iOS7
fromView = fromViewController.view;
toView = toViewController.view;
}
BOOL isPresenting = (toViewController.presentingViewController == fromViewController);
CGRect fromFrame = [transitionContext initialFrameForViewController:fromViewController];
CGRect toFrame = [transitionContext finalFrameForViewController:toViewController];
// Based on our configured targetEdge, derive a normalized vector that will
// be used to offset the frame of the presented view controller.
__block CGVector offset;
if (self.targetEdge == UIRectEdgeTop)
offset = CGVectorMake(0.f, 1.f);
else if (self.targetEdge == UIRectEdgeBottom)
offset = CGVectorMake(0.f, -1.f);
else if (self.targetEdge == UIRectEdgeLeft)
offset = CGVectorMake(1.f, 0.f);
else if (self.targetEdge == UIRectEdgeRight)
offset = CGVectorMake(-1.f, 0.f);
else
NSAssert(NO, @"targetEdge must be one of UIRectEdgeTop, UIRectEdgeBottom, UIRectEdgeLeft, or UIRectEdgeRight.");
if (isPresenting)
{
// For a presentation, the toView starts off-screen and slides in.
fromView.frame = fromFrame;
toView.frame = CGRectOffset(toFrame, toFrame.size.width * offset.dx * -1,
toFrame.size.height * offset.dy * -1);
}
else
{
fromView.frame = fromFrame;
toView.frame = toFrame;
}
// We are responsible for adding the incoming view to the containerView
// for the presentation.
if (isPresenting)
{
[containerView addSubview:toView];
}
else
{
// -addSubview places its argument at the front of the subview stack.
// For a dismissal animation we want the fromView to slide away,
// revealing the toView. Thus we must place toView under the fromView.
[containerView insertSubview:toView belowSubview:fromView];
}
NSTimeInterval transitionDuration = [self transitionDuration:transitionContext];
//動畫
[UIView animateWithDuration:transitionDuration animations:^{
if (isPresenting)
{
toView.frame = toFrame;
}
else
{
// For a dismissal, the fromView slides off the screen.
fromView.frame = CGRectOffset(fromFrame, fromFrame.size.width * offset.dx,
fromFrame.size.height * offset.dy);
}
} completion:^(BOOL finished) {
BOOL wasCancelled = [transitionContext transitionWasCancelled];
if (wasCancelled)
{
[toView removeFromSuperview];
}
[transitionContext completeTransition:!wasCancelled];
}];;
}
使用的地方生成自定義轉場代理,將需要present的控制器的transitioningDelegate設置為此代理就可以看到presentViewController有了push的效果,dismiss有了pop的動畫效果。