UIKitiOS开发部落demo

iOS自定义转场动画/UIPresentationControl

2017-03-09  本文已影响1906人  苦笑男神

一.UIPresentationController简介

苹果官方Demo.gif

二.UIPresentationController作用

AViewController *vc = [[AViewController alloc] init];
[self presentViewController:vc animated:YES completion:nil];

三.UIPresentationController方法和属性介绍

/**
 构造方法,苹果建议使用这个初始化UIPresentationController
 @param presentedViewController 将要跳转到的目标控制器
 @param presentingViewController 跳转前的原控制器
 */
- (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController;

presentedViewController:     要 modal 显示的视图控制器
presentingViewController:    跳转前视图控制器
containerView()              容器视图
presentedView()              被展现控制器的视图

func presentationTransitionWillBegin()          跳转将要开始
func presentationTransitionDidEnd(completed: Bool)  跳转完成
func dismissalTransitionWillBegin()         dismiss将要开始
func dismissalTransitionDidEnd(completed: Bool)     dismiss完成
func frameOfPresentedViewInContainerView()    动画之后,目标控制器View的位置

四.自定义modal转场动画的第三个步骤

五.自定义modal转场动画第一个Demo

#import <UIKit/UIKit.h>
/**
 * 实现自定义过渡动画:
 * 1.继承UIPresentationController 成为子类
 * 2.遵守UIViewControllerAnimatedTransitioning 协议
 * 其实也可以写成两个类,分别继承UIPresentationController和实现UIViewControllerAnimatedTransitioning协议
 */
@interface BCustomPresentationController : UIPresentationController <UIViewControllerTransitioningDelegate>
/** 黑色半透明背景 */
@property (nonatomic, strong) UIView *dimmingView;
@end
@implementation BCustomPresentationController

//| ------------------------------第一步内容----------------------------------------------
#pragma mark - UIViewControllerTransitioningDelegate
/*
 * 来告诉控制器,谁是动画主管(UIPresentationController),因为此类继承了UIPresentationController,就返回了self
 */
- (UIPresentationController* )presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source {
    return self;
}

//| ------------------------------第二步内容----------------------------------------------
#pragma mark - 重写UIPresentationController个别方法
- (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController {
    self = [super initWithPresentedViewController:presentedViewController presentingViewController:presentingViewController];
    if (self) {
        // 必须设置 presentedViewController 的 modalPresentationStyle
        // 在自定义动画效果的情况下,苹果强烈建议设置为 UIModalPresentationCustom
        presentedViewController.modalPresentationStyle = UIModalPresentationCustom;
    }
    return self;
}

// 呈现过渡即将开始的时候被调用的
// 可以在此方法创建和设置自定义动画所需的view
- (void)presentationTransitionWillBegin {
    // 背景遮罩
    UIView *dimmingView = [[UIView alloc] initWithFrame:self.containerView.bounds];
    dimmingView.backgroundColor = [UIColor blackColor];
    dimmingView.opaque = NO; //是否透明
    dimmingView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    [dimmingView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dimmingViewTapped:)]];
    self.dimmingView = dimmingView;
    
    [self.containerView addSubview:dimmingView]; // 添加到动画容器View中。
    
    // 获取presentingViewController 的转换协调器,应该动画期间的一个类?上下文?之类的,负责动画的一个东西
    id<UIViewControllerTransitionCoordinator> transitionCoordinator = self.presentingViewController.transitionCoordinator;
    
    // 动画期间,背景View的动画方式
    self.dimmingView.alpha = 0.f;
    [transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        self.dimmingView.alpha = 0.4f;
    } completion:NULL];
}

#pragma mark 点击了背景遮罩view
- (void)dimmingViewTapped:(UITapGestureRecognizer*)sender {
    [self.presentingViewController dismissViewControllerAnimated:YES completion:NULL];
}

// 在呈现过渡结束时被调用的,并且该方法提供一个布尔变量来判断过渡效果是否完成
- (void)presentationTransitionDidEnd:(BOOL)completed {
    // 在取消动画的情况下,可能为NO,这种情况下,应该取消视图的引用,防止视图没有释放
    if (!completed) {
        self.dimmingView = nil;
    }
}

// 消失过渡即将开始的时候被调用的
- (void)dismissalTransitionWillBegin {
    id<UIViewControllerTransitionCoordinator> transitionCoordinator = self.presentingViewController.transitionCoordinator;
    
    [transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        self.dimmingView.alpha = 0.f;
    } completion:NULL];
}

// 消失过渡完成之后调用,此时应该将视图移除,防止强引用
- (void)dismissalTransitionDidEnd:(BOOL)completed {
    if (completed == YES) {
        [self.dimmingView removeFromSuperview];
        self.dimmingView = nil;
    }
}

// 返回目标控制器Viewframe
- (CGRect)frameOfPresentedViewInContainerView {
    // 这里直接按照想要的大小写死,其实这样写不好,在第二个Demo里,我们将按照苹果官方Demo,写灵活的获取方式。
    CGFloat height = 300.f;
    
    CGRect containerViewBounds = self.containerView.bounds;
    containerViewBounds.origin.y = containerViewBounds.size.height - height;
    containerViewBounds.size.height = height;
    return containerViewBounds;
}

//  建议就这样重写就行,这个应该是控制器内容大小变化时,就会调用这个方法, 比如适配横竖屏幕时,翻转屏幕时
//  可以使用UIContentContainer的方法来调整任何子视图控制器的大小或位置。
- (void)preferredContentSizeDidChangeForChildContentContainer:(id<UIContentContainer>)container {
    [super preferredContentSizeDidChangeForChildContentContainer:container];
    if (container == self.presentedViewController) [self.containerView setNeedsLayout];
}

- (void)containerViewWillLayoutSubviews {
    [super containerViewWillLayoutSubviews];
    self.dimmingView.frame = self.containerView.bounds;
}
@end

六.自定义modal转场动画第二个Demo

/| ------------------------------第三步内容----------------------------------------------
#pragma mark UIViewControllerAnimatedTransitioning具体动画实现
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
    // 动画时长
    return 0.45 ;
}

// 核心,动画效果的实现
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
    // 1.获取源控制器、目标控制器、动画容器View
    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    __unused UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    UIView *containerView = transitionContext.containerView;
    
    // 2. 获取源控制器、目标控制器 的View,但是注意二者在开始动画,消失动画,身份是不一样的:
    // 也可以直接通过上面获取控制器获取,比如:toViewController.view
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    
    [containerView addSubview:toView];  //必须添加到动画容器View上。

    // 3.设置动画具体细节,使用[UIView animate...]动画,或者其他方式呈现动画。

    // 4.动画结束时,必须执行下句代码
    [transitionContext completeTransition:YES];    
}

- (void)animationEnded:(BOOL) transitionCompleted {
    // 动画结束...
}

七.自定义modal转场动画第三个Demo

toVC.modalPresentationStyle = UIModalPresentationCustom ; //必须是UIModalPresentationCustom
toVC.view.backgroundColor = [UIColor clearColor];  //必须是clearColor
[self presentViewController:toVC animated:NO completion:nil]; //animated:必须是NO
#pragma mark - 在此方法做动画呈现
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    self.bgView.frame = [UIScreen mainScreen].bounds;
    self.contentView.frame = CGRectMake(0,[UIScreen mainScreen].bounds.size.height, CGRectGetWidth([UIScreen mainScreen].bounds), 300.0);
    
    [UIView animateWithDuration:0.4 delay:0 usingSpringWithDamping:0.95 initialSpringVelocity:0.05 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        
        self.bgView.alpha = 1.0f ;
        self.contentView.frame = CGRectMake(0, [UIScreen mainScreen].bounds.size.height - 300.f, CGRectGetWidth([UIScreen mainScreen].bounds), 300.0f);
        
    } completion:^(BOOL finished) {
    }];
}

#pragma mark - 消失
- (void)clickBgView {
    [UIView animateWithDuration:0.4 delay:0 usingSpringWithDamping:0.95 initialSpringVelocity:0.05 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        
        self.bgView.alpha = 0.0f ;
        self.contentView.frame = CGRectMake(0,[UIScreen mainScreen].bounds.size.height, CGRectGetWidth([UIScreen mainScreen].bounds), 300.0);
        
    } completion:^(BOOL finished) {
        // 动画Animated必须是NO,不然消失之后,会有0.35s时间,再点击无效
        [self dismissViewControllerAnimated:NO completion:nil];
    }];
}

END。
我是小侯爷。
在魔都艰苦奋斗,白天是上班族,晚上是知识服务工作者。
如果读完觉得有收获的话,记得关注和点赞哦。
非要打赏的话,我也是不会拒绝的。

上一篇下一篇

猜你喜欢

热点阅读