先上最终的效果图吧
这是A视图控制器将B视图控制器 present出来的一个效果,系统默认的present的样式是B从下往上出来,并且在B完全显示之后A是不可见的,所以要实现这种效果必须自定义样式了。
要自定义presentation风格,B需要先遵守协议UIViewControllerTransitioningDelegate
、设置modalPresentationStyle和transitioningDelegate。
self.modalPresentationStyle = UIModalPresentationCustom;
self.transitioningDelegate = self;
这两句代码需要放到init
方法中。
要实现示例中的效果,我们可以分为以下几个步骤:
- 使A的view始终可见。
- 自定义present的动画。
- 自定义dismiss的动画。
使A的view始终可见
创建UIPresentationController
的子类MyPresentationController
,重写方法shouldRemovePresentersView
.
@implementation MyPresentationController
- (BOOL)shouldRemovePresentersView {
return NO;
}
@end
在B 中实现协议UIViewControllerTransitioningDelegate
的方法
- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source
{
return [[MyPresentationController alloc]initWithPresentedViewController:presented presentingViewController:presenting];
}
这样我们第一步完成,效果如图
自定义present的动画
创建一个NSObject的子类BounceAnimationController
,使其遵守协议UIViewControllerAnimatedTransitioning
,并实现协议中相应的方法。
@implementation BounceAnimationController
//指明动画将执行多长时间
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext
{
return 0.4;
}
//指明从A切入到B的过程中将执行的方法。
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{
//toViewController:即B视图控制器
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
//toView:B视图控制器的view
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
//containView:可以理解为它管理着所有做转场动画的视图,必须将toView加入到其中
UIView *containView = [transitionContext containerView];
if (containView != nil && toView != nil && toViewController != nil) {
toView.frame = [transitionContext finalFrameForViewController:toViewController];
[containView addSubview:toView];
//contentView:示例中的弹框,因为我只需对弹框做动画
UIView *contentView = [toView viewWithTag:1000];
contentView.transform = CGAffineTransformMakeScale(0.7, 0.7);
//以下是keyframe动画
[UIView animateKeyframesWithDuration:[self transitionDuration:transitionContext] delay:0 options:UIViewKeyframeAnimationOptionCalculationModeCubic animations:^{
[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0.334 animations:^{
contentView.transform = CGAffineTransformMakeScale(1.2, 1.2);
}];
[UIView addKeyframeWithRelativeStartTime:0.334 relativeDuration:0.333 animations:^{
contentView.transform = CGAffineTransformMakeScale(0.9, 0.9);
}];
[UIView addKeyframeWithRelativeStartTime:0.667 relativeDuration:0.333 animations:^{
contentView.transform = CGAffineTransformMakeScale(1.0, 1.0);
}];
} completion:^(BOOL finished) {
//报告动画完成
[transitionContext completeTransition:finished];
}];
}
}
@end
在B中实现协议UIViewControllerTransitioningDelegate
中的方法告诉代理VC在present的时候使用自定义的切换效果。
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
return [[BounceAnimationController alloc]init];
}
这样我们第二步就完成了,来看看效果:
最后一步:自定义dismiss的动画
示例中dismiss是直接将B的view的透明度以动画的形式设置为0.创建NSObject的子类FadeOutAnimationController
,使其遵守协议UIViewControllerAnimatedTransitioning
,并实现协议中相应的方法。由于和自定义present动画几乎一样,所以我只将动画部分代码贴出来。
//因为是dismiss的时候是从B切换到A,所以此时fromView是B的view
UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
NSTimeInterval duration = [self transitionDuration:transitionContext];
if (fromView != nil) {
[UIView animateWithDuration:duration animations:^{
fromView.alpha = 0;
} completion:^(BOOL finished) {
[transitionContext completeTransition:finished];
}];
}
在B中实现协议UIViewControllerTransitioningDelegate
中的方法告诉代理VC在dismiss的时候使用自定义的切换效果。
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
return [[FadeOutAnimationController alloc]init];
}
到这里我们的自定义viewController切换动画就完成啦。