[iOS] 转场动画

这个topic其实起源于看到了我们别的组的一个小效果,就是首页有一个小标签,标签点开会以圆形扩散的效果打开一个付费页~ 类似酱紫:

转场动画.gif (图片借鉴View-Controller-Transition-PartIII里的)

页面的切换效果,其实就是转场动画。

官方支持以下几种方式的自定义转场:

  1. UINavigationController 中 push 和 pop
  2. UITabBarController 中切换 Tab
  3. presentViewController以及dismiss(modal模态转场)
  4. UICollectionViewController 的布局转场:UICollectionViewController 与 UINavigationController 结合的转场方式 (https://github.com/seedante/iOS-Note/wiki/View-Controller-Transition-PartIII#Chapter4)

我之前做过一个效果就是切换tab的时候from和to有一个渐隐和渐现的效果,并且如果切换方向不同,from和to移动方向也不一样,其实就是利用了转场动画。


1. 自定义转场动画

自定义转场其实就是通过UIViewControllerAnimatedTransitioning协议实现的,所以我们需要先建一个实现了UIViewControllerAnimatedTransitioning的NSObject:

#import "TransAnimation.h"
@import UIKit;

@interface TransAnimation ()<UIViewControllerAnimatedTransitioning>

@end

@implementation TransAnimation

- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
}

- (NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext {
    return 0.5;
}

这里的transitionDuration就是把转场动画的时间返回,然后animateTransition就是真的做动画。

如果像上面那样把animateTransition写成一个空block,那么当你点击跳转另一个VC的时候会发现木有反应哦,因为你并木有实现转场所以VC不会显示

那么要怎么转呢,例如酱紫:

- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
    // 获取fromVc和toVc
    UIViewController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    UIView *fromView = [[UIView alloc] init];;
    UIView *toView = [[UIView alloc] init];

    if ([transitionContext respondsToSelector:@selector(viewForKey:)]) {
        // fromVc 的view
        fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
        // toVc的view
        toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    } else {
        // fromVc 的view
        fromView = fromVc.view;
        // toVc的view
        toView =toVc.view;
    }

    CGFloat x = [UIScreen mainScreen].bounds.size.width;
    
    // 转场环境
    UIView *containView = [transitionContext containerView];
    toView.frame = CGRectMake(-x, 0, containView.frame.size.width, containView.frame.size.height);

    [containView addSubview:fromView];
    [containView addSubview:toView];

    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        fromView.transform = CGAffineTransformTranslate(fromView.transform, x, 0);
        toView.transform = CGAffineTransformTranslate(toView.transform, x, 0);
    } completion:^(BOOL finished) {
        [transitionContext completeTransition:YES];
    }];
}

那么如果animateTransition里实际做动画的时长与transitionDuration返回的时间不一致会怎样呢?是会按照animateTransition里面的把动画乖乖做完的~ 不会到了animateTransition就把动画掐掉哒
苹果大大告诉我们:

UIKit calls this method to obtain the timing information for your animations. The value you provide should be the same value that you use when configuring the animations in your animateTransition method. UIKit uses the value to synchronize the actions of other objects that might be involved in the transition. For example, a navigation controller uses the value to synchronize changes to the navigation bar.

When determining the value to return, assume there will be no user interaction during the transition—even if you plan to support user interactions at runtime.

来划重点啦~

  • animateTransition里实际做动画的时长与transitionDuration返回的时间必须一致,否则可能其他参与转场的组件例如navigation bar在动画没做完或者已经做完很久了才显示
  • transitionDuration这个时间不需要考虑交互转场的影响,按照非交互即可,即使你真的要用交互转场

另外一个关于animateTransition的注释是酱紫的:

// This method can only  be a nop if the transition is interactive and not a percentDriven interactive transition.
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
  • 也就是说animateTransition只有在是交互式并且不是百分比交互式的情况下才可以是空哦

  • 另外一个注意点是,如果你在A push B的时候加了透明度变化的A渐隐,B渐现的动画,在动画结束需要让A的透明度置位1,否则用户从B pop回A的时候就是黑色没有页面啦:

[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
  fromView.alpha = 0;
  toView.alpha = 1;
} completion:^(BOOL finished) {
  [transitionContext completeTransition:YES];
  fromView.alpha = 1;
}];

※ 圆形扩散转场

很多很炫的转场都是通过截屏实现的,比如开头的一个圆形扩散,就是把toView截屏,然后以圆形的masklayer逐渐显示。划重点,用layer.mask实现遮罩,外加CAShapeLayer可以做path动画

[fromView snapshotViewAfterScreenUpdates:NO];
[toView snapshotViewAfterScreenUpdates:YES];

注意snapshotViewAfterScreenUpdates的参数就是是不是要立刻截屏,如果yes一般是给fromView的因为转场的时候它就显示着,而对toView而言需要先add然后截屏,所以都选NO。

我们有些开门的动效,就是利用把fromView截图,然后生成两个UIView半屏,把全屏截图放进去作为子view,分别clipToBounds,就得到了两边两个view,然后做位移动画即可~

而有些放大缩小的例如点一个图片全屏就是把toView截屏做放大缩小动画,和圆形扩散类似只是换为了位移大小变化动画。

这里提供举个定点圆形扩散的转场animator:

// .h
typedef NS_ENUM(NSUInteger, TransitionType) {
    TransitionTypePresent = 0, //管理present动画
    TransitionTypeDissmis,
};

@interface TransAnimation : NSObject<UIViewControllerAnimatedTransitioning>

@property(nonatomic) TransitionType transitionType;

@end

// .m
#import "TransAnimation.h"

@interface TransAnimation ()<CAAnimationDelegate>

@property (nonatomic) id<UIViewControllerContextTransitioning> transitionContext;

@end

@implementation TransAnimation

- (NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext {
    return 3;
}

- (void)animateTransition:
(id<UIViewControllerContextTransitioning>)transitionContext {
    if (self.transitionType == TransitionTypePresent) {
        [self presentAnimation:transitionContext];
    } else if (self.transitionType == TransitionTypeDissmis) {
        [self dismissAnimation:transitionContext];
    }
}

- (void)presentAnimation:
(id<UIViewControllerContextTransitioning>)transitionContext {
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *tempView = [toVC.view snapshotViewAfterScreenUpdates:YES];
    UIView *containerView = [transitionContext containerView];
    
    [containerView addSubview:toVC.view];
    [containerView addSubview:fromVC.view];
    [containerView addSubview:tempView];
    
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
    CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
    
    
    CGRect rect = CGRectMake(50, [UIScreen mainScreen].bounds.size.height - 50, 2, 2);
    
    UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:rect];
    UIBezierPath *endPath = [UIBezierPath bezierPathWithArcCenter:containerView.center radius:sqrt(screenHeight * screenHeight + screenWidth * screenWidth)  startAngle:0 endAngle:M_PI*2 clockwise:YES];
    
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.path = endPath.CGPath;
    tempView.layer.mask = maskLayer;
    
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
    animation.delegate = self;
    
    animation.fromValue = (__bridge id)(startPath.CGPath);
    animation.toValue = (__bridge id)((endPath.CGPath));
    animation.duration = [self transitionDuration:transitionContext];
    animation.timingFunction = [CAMediaTimingFunction  functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    [maskLayer addAnimation:animation forKey:@"PointNextPath"];
    self.transitionContext = transitionContext;
}

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    UIView *containerView = [self.transitionContext containerView];
    [containerView.subviews.lastObject removeFromSuperview];
    [self.transitionContext completeTransition:YES];
}

- (void)dismissAnimation:
(id<UIViewControllerContextTransitioning>)transitionContext {
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    UIView *tempView = [fromVC.view snapshotViewAfterScreenUpdates:YES];
    UIView *containerView = [transitionContext containerView];
    
    [containerView addSubview:toVC.view];
    [containerView addSubview:tempView];
    
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
    CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
    
    
    CGRect rect = CGRectMake(50, [UIScreen mainScreen].bounds.size.height - 50, 2, 2);
    
    UIBezierPath *endPath = [UIBezierPath bezierPathWithOvalInRect:rect];
    UIBezierPath *startPath = [UIBezierPath bezierPathWithArcCenter:containerView.center radius:sqrt(screenHeight * screenHeight + screenWidth * screenWidth)  startAngle:0 endAngle:M_PI*2 clockwise:YES];
    
    CAShapeLayer *maskLayer = [CAShapeLayer layer];
    maskLayer.path = endPath.CGPath;
    tempView.layer.mask = maskLayer;
    
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
    animation.delegate = self;
    
    animation.fromValue = (__bridge id)(startPath.CGPath);
    animation.toValue = (__bridge id)((endPath.CGPath));
    animation.duration = [self transitionDuration:transitionContext];
    animation.timingFunction = [CAMediaTimingFunction  functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    [maskLayer addAnimation:animation forKey:@"PointNextPath"];
    self.transitionContext = transitionContext;
}

@end

效果就是酱紫的:


圆形扩散收缩

2. 设置动画

现在我们有了动画,要怎么设置给navigation或者present作为效果呢?先看navigation的~

2.1 UINavigationController 中 push 和 pop

可参考:https://blog.csdn.net/dolacmeng/article/details/51873395?utm_source=blogxgwz0

  • 首先我们需要设置navigationController的delegate,这个delegate可以指定动画是啥
@interface MainViewController ()<UINavigationControllerDelegate>
@end

@implementation MainViewController

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    self.navigationController.delegate = self;
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    if (self.navigationController.delegate == self) {
        self.navigationController.delegate = nil;
    }
}

@end
  • 指定动画是啥的方式,这里指定为[[TransAnimation alloc] init]
// MARK: - UINavigationControllerDelegate
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
    if (fromVC == self && [toVC isKindOfClass:[MasViewController class]]) {
        return [[TransAnimation alloc] init];
    }
    
    return nil;
}

注意哦,这里其实可以通过fromVCtoVC判断是push还是pop哈

  • 最后就是只要你让TransAnimation实现了UIViewControllerAnimatedTransitioning即可啦~

2.2 UITabBarController 中切换 Tab

可参考:https://blog.csdn.net/qq_25639809/article/details/61198894

实现UITabBarControllerDelegateanimationControllerForTransitionFromViewController把动画返回回去就好啦~~

@interface DMMainViewController ()<UITabBarControllerDelegate>

@end

@implementation DMMainViewController

// 实现协议
- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
    return [[AnimationManager alloc] init];
}

@end

这里一样的可以通过fromVC和toVC判断方向~


2.3 presentViewController以及dismiss

可参考:https://www.jianshu.com/p/15355cc8e133

和nav以及tab非常类似,也是通过delegate来设置动画,区别大概只是delegate里面会分是present还是dismiss,不用你自己判断。

例如你需要从A跳到B,那么你需要做这样的事情:

// VC A
ViewControllerB * bVC = [[ViewControllerB alloc] init];
bVC.transitioningDelegate = self;
[self presentViewController: bVC animated:YES completion:nil];

注意这里把B的transitioningDelegate设为了A了哦

这里A需要实现的delegate就是UIViewControllerTransitioningDelegate

//指定present动画
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
//指定dismiss动画
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
//指定交互式present动画的控制类
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator;
//指定交互式dismiss动画的控制类
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator;

所以这里举例如果我们想让A跳到B的时候有动画只要酱紫就好啦:

- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
    return [[TransAnimation alloc] init];
}

注意如果是present的动画,就在present的时候改delegate,然后实现animationControllerForPresentedController,如果想改dismiss的动画,就确认当前VC的delegate的dismiss是实现了的

例如当前页面显示的时候将delegate设为自己:

- (void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    self.transitioningDelegate = self;
}

- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
    return [[TransAnimation alloc] init];
}

2.4 UICollectionViewController 的布局转场

这个我本来想找个OC的,结果真的木有。。就还是swift的参考啦其实都差不多。。https://github.com/seedante/iOS-Note/wiki/View-Controller-Transition-PartIII#Chapter4

从iOS7开始,在collectionViewController中就伴随着自定义转场的功能产生了一个新的属性:useLayoutToLayoutNavigationTransitions,这是一个BOOL值,如果设置该值为YES,如果navigationController push或者pop 一个collectionViewController 到另一个collectionViewController的时候,其所在的navigationController就可以用collectionView的布局转场动画来替换标准的转场,这点大家可以自行尝试一下,但是显然,这个属性的致命的局限性就是你得必须满足都是collectionViewController,对于collectionView就没办法了。

这个的效果其实就类似我们的照片app里面那种,从一个collection到一个新的collection的平滑过渡,而且只要打开一个熟悉超厉害~

demo引用别人的哈,实在懒得写了sorry:https://github.com/seedante/iOS-ViewController-Transition-Demo/tree/master/CollectionViewControllerLayoutTransition
(注意在这个demo有点问题,没有给collectionView register cell,需要改一下哈要不会在false的时候crash)

左侧是useLayoutToLayoutNavigationTransitions为true,右侧是false.gif

注意需要在pop或者push或者present或者dismiss之前设置delegate哦,否则是不能触发animation哒


3. 可交互转场动画

什么叫可交互转场动画呢?就是根据你的手指位置来控制转场动画进度,类似酱紫:

可交互转场动画

所以要怎么实现呢?

① 首先得准备一个animation,和Part1一样,需要实现UIViewControllerAnimatedTransitioning作为转场动画
// .h
@interface TransAnimation : NSObject<UIViewControllerAnimatedTransitioning>

@end

// .m
@interface TransAnimation ()

@end

@implementation TransAnimation

- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
    // 获取fromVc和toVc
    UIViewController *fromVc = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVc = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

    UIView *fromView = [[UIView alloc] init];;
    UIView *toView = [[UIView alloc] init];

    if ([transitionContext respondsToSelector:@selector(viewForKey:)]) {
        // fromVc 的view
        fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
        // toVc的view
        toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    } else {
        // fromVc 的view
        fromView = fromVc.view;
        // toVc的view
        toView =toVc.view;
    }
    
    // 转场环境
    UIView *containView = [transitionContext containerView];
    toView.alpha = 0;

    [containView addSubview:fromView];
    [containView addSubview:toView];

    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        fromView.alpha = 0;
        toView.alpha = 1;
    } completion:^(BOOL finished) {
        // 由于是交互的,所以completeTransition不一定会success
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        if ([transitionContext transitionWasCancelled]) {
            //手势取消了
        }else{
            //手势成功
        }
        fromView.alpha = 1;
    }];
}

- (NSTimeInterval)transitionDuration:(nullable id<UIViewControllerContextTransitioning>)transitionContext {
    return 0.5;
}

@end

交互式转场的原理就是,更新转场百分比(UIPercentDrivenInteractiveTransition对象),会自动根据百分比算出animateTransition里面的动画进行到什么状态比如alpha、frame之类的然后显示出来。

也就是交互式的转场也是依赖这个animator,只不过附加的百分比计算动画进度并更新,不是让动画自己动。(自己动就是定时器模式啦)

这里例子里是一个渐变的animator,需要注意的点是之前非交互的animator是动画做完转场就一定完成了,但是交互式是不一样的。交互式的松手的时候无论是成功转场还是失败,都会执行动画的completion,所以在里面需要判断[transitionContext transitionWasCancelled]是不是转场取消了

一个疑惑点木有想明白,我们在animateTransition里面写什么动画或者不写动画都是未知的,系统是怎么检测到我们写的动画并在传入百分比的时候按照百分比计算显示的呢?但毕竟正常做动画也是系统算的,只是加一个百分比也很容易对于系统而言

② 设置interactionController

可交互转场动画依赖于一个百分比管理器,也就是interactionController,这个管理器需要实现UIViewControllerInteractiveTransitioning协议,官方给我们提供了一个现成的UIPercentDrivenInteractiveTransition类,你也可以继承UIPercentDrivenInteractiveTransition来使用。

UIViewControllerInteractiveTransitioning协议的功能主要是控制转场动画的状态,即动画完成的百分比,所以只有在转场中才有用。

比如我们通过[self.navigationController popViewControllerAnimated:YES]触发pop转场动画,然后在转场动画结束之前通过- (void)updateInteractiveTransition:(CGFloat)percentComplete更改转场动画的完成的百分比,那么转场动画将由实现UIViewControllerInteractiveTransitioning的类接管,而不是由定时器管理,之后就可以随意设置动画状态了。

交互动画往往配合手势操作,手势操作产生一序列百分比数通过updateInteractiveTransition方法实时更新转场动画状态。

  • 这一步要注意的是,对于navigation的push/pop或者是presentVC和dismiss,设置interactionController的方式都是不一样的哦!也要记得把navigationController.delegate设置上哦,可以在push的时候设置

举个例子:

// dismiss
-(id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator;

// pop push
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController;

这些方法对应不同的触发条件,[self dismissViewControllerAnimated:YES completion:nil]触发的是上面的,[self.navigationController popViewControllerAnimated:YES];触发的是下面的~

这类方法返回的都是一个id<UIViewControllerInteractiveTransitioning>对象,也就是我们的百分比控制器。

例如:

@property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactiveTransition;

self.interactiveTransition = [UIPercentDrivenInteractiveTransition new];

// MARK: - UINavigationControllerDelegate
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
    return self.interactiveTransition;
}

即使是可交互转场动画也是动画,需要在delegate返回转场动画的方法里面返回我们①里面创建的animator哦

例如navigation的:

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
    if (operation == UINavigationControllerOperationPop) {
        return [[TransAnimation alloc] init];
    }
    
    return nil;
}
③ 添加手势更新百分比

现在有了百分比控制器,那么怎么更新转场进行到了百分之几呢,这就依赖于手势啦~

UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc] init];
[pan addTarget:self action:@selector(panGestureRecognizerAction:)];
[self.view addGestureRecognizer:pan];

- (void)panGestureRecognizerAction:(UIPanGestureRecognizer *)pan{
   //产生百分比
   CGFloat process = [pan translationInView:self.view].x / ([UIScreen mainScreen].bounds.size.width);
   
   process = MIN(1.0,(MAX(0.0, process)));
   
   if (pan.state == UIGestureRecognizerStateBegan) {
       self.interactiveTransition = [UIPercentDrivenInteractiveTransition new];
       // 注意这里一定要用pop才能触发,因为delegate实现的是navigation的`interactionControllerForAnimationController`
       [self.navigationController  popViewControllerAnimated:YES];
   }else if (pan.state == UIGestureRecognizerStateChanged){
       [self.interactiveTransition updateInteractiveTransition:process];
   }else if (pan.state == UIGestureRecognizerStateEnded
             || pan.state == UIGestureRecognizerStateCancelled){
       if (process > 0.5) {
           [ self.interactiveTransition finishInteractiveTransition];
       }else{
           [ self.interactiveTransition cancelInteractiveTransition];
       }
       self.interactiveTransition = nil;
   }
}

UIGestureRecognizerStateBegan的时候需要触发动画,所以需要用popViewControllerAnimated,并创建UIPercentDrivenInteractiveTransition

然后在UIGestureRecognizerStateChanged的时候更新当前百分比控制器的百分比updateInteractiveTransition

UIGestureRecognizerStateEnded的时候选择是finish还是cancelinteractiveTransition,如果cancel的话就说明转场失败~ finish就是成功哦

到这里就可以实现渐变的交互转场啦撒花花~~~


引用的别人的图说明一下转场的询问顺序:

pop push的时候的动画询问流程

也就是如果有自定义动画并没有交互动画就会把动画交给定时器,按照自定义动画执行~ 如果有就会按照百分比执行。

这里也说明了一定要先实现转场animationControllerForOperation的delegate才会有可交互的转场一说,毕竟可交互的转场只是在原来转场的基础上增加了百分比控制器interactiveTransition

补充一个坑:https://stackoverflow.com/questions/25488267/custom-transition-animation-not-calling-vc-lifecycle-methods-on-dismiss

Reference:

  1. 超全的一个文~ 很喜欢的:https://www.jianshu.com/p/ec08f43808aa
  2. https://www.jianshu.com/p/a9b1307b305b
  3. https://www.jianshu.com/p/cca1dcb79ddf
  4. https://www.jianshu.com/p/29b0165de712?from=groupmessage
  5. 动画合集:https://www.jianshu.com/p/fd3154946919
  6. tableview push collectionViewController:https://www.jianshu.com/p/c609ebc6a433
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容

  • 直接看转场动画的协议时有点迷糊,做了一个简单的demo记录下理解过程 一、如何更改动画方式 站在自己的角度,最简单...
    10m每秒滑行阅读 340评论 0 0
  • 转场动画,就是Vc切换过程中的过渡动画。官方支持以下几种方式的自定义转场:1、我们最常见的在 UINavigati...
    炎成阅读 13,074评论 0 33
  • 首先看一个简单的 效果图模拟器不知道怎么了 变得这么慢,就将就着看吧 关于转场动画我们主要考虑两个,一个是导航条中...
    LeeDev阅读 1,477评论 1 5
  • 转场动画学习中...TransitionDemo代码 实现自定义的转场动画(只涉及自定义动画,不管手势驱动) 涉及...
    YaoYaoX阅读 687评论 0 3
  • 概述 这篇文章,我将讲述几种转场动画的自定义方式,并且每种方式附上一个示例,毕竟代码才是我们的语言,这样比较容易上...
    伯恩的遗产阅读 53,844评论 37 381