xml地图|网站地图|网站标签 [设为首页] [加入收藏]

自定义转场动画实现导航栏渐变效果,iOS自定义

来源:http://www.ccidsi.com 作者:呼叫中心培训课程 人气:128 发布时间:2020-04-28
摘要:效果图.gif 本文完整Demo请点击这里 DEMO ONE:贰个美妙移动作效果果push动漫,扶植手势pop 玄妙效果.gif Push 转场效果 view.frame 从右屏外平移到显示器中间 父 VC 光滑度从 1.0 变成 0.5 父 VC

图片 1效果图.gif

本文完整Demo请点击这里

DEMO ONE:贰个美妙移动作效果果push动漫,扶植手势pop

图片 2

玄妙效果.gif

  1. Push转场效果

    • view.frame从右屏外平移到显示器中间
    • VC光滑度从1.0变成0.5
    • VC向左平移110
    • 时长0.3s
  2. Pop转场效果

    • view.frame从显示器正中活动到右屏外
    • VC折射率从0.5变成1.0
    • VC向右平移110
    • 时长0.4s
    • VCself.view急需增加UIScreenEdgePanGestureRecognizer边缘手势,平移当先显示器中线则dismiss,反之回弹回去。
  3. 实现:<UIViewControllerAnimatedTransitioning>协议UIPercentDrivenInteractiveTransition手势过渡管理对象

前言

运用系统的转场动画,不可能决定动漫整个动漫进程,无法针对系统导航栏举行颜色过渡效果,故此篇文章选用自定义转场动漫的艺术,把控整个动漫进程,实现导航栏颜色平滑的连结效果,实效如下所示。

图片 3

DEMO THREE:一个翻页push效果,襄帮手势PUSH和POP

图片 4

翻页效果.gif

图片 5violet.jpg

新建一个类KJPopAnimator坚守UIViewControllerAnimatedTransitioning

(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return 0.25;
}
长期以来动漫时间概念为0.25秒,另叁个共谋方式如下:

  • (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {

      UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
      UIViewController  *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
      UIView *containerView = [transitionContext containerView];
      UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
      toView.kj_x = -100;

      UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
      fromView.layer.shadowRadius = 8;
      fromView.layer.shadowColor = [UIColor blackColor].CGColor;
      fromView.layer.shadowOpacity = 0.5;

      UIView *maskView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
      maskView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.15];


      [containerView addSubview:toView];
      [containerView addSubview:maskView];
      [containerView addSubview:fromView];

      CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"shadowOpacity"];
      animation.duration = kTransitionDuration;
      animation.removedOnCompletion = YES;
      animation.toValue = @0;
      animation.delegate = self;
      [fromView.layer addAnimation:animation forKey:nil];

      UINavigationBar *navigationBar = toVC.navigationController.navigationBar;
      [navigationBar kj_setBackgroundColor:fromVC.kj_navigationBarTintColor];
      [navigationBar kj_setNavigationBarAlpha:fromVC.kj_navigationBarAlpha];
      [navigationBar kj_setNavigationTitleColor:fromVC.kj_navigationTitleColor];


      [UIView animateWithDuration:kTransitionDuration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
          toView.kj_x = 0;
          fromView.kj_x = [UIScreen mainScreen].bounds.size.width;
          maskView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0];

          [navigationBar kj_setBackgroundColor:toVC.kj_navigationBarTintColor];
          [navigationBar kj_setNavigationBarAlpha:toVC.kj_navigationBarAlpha];
          [navigationBar kj_setNavigationTitleColor:toVC.kj_navigationTitleColor];

      } completion:^(BOOL finished) {
          [maskView removeFromSuperview];
          fromView.layer.shadowOpacity = 0;
          [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
      }];

}

落实上海高校概和push差不离,这里为了和类别的pop动漫保持一致,还加了pop时的视图阴影以致叁个深灰透明的遮罩层(读者能够自个儿打开几个app观看系统的pop动漫,建议手势左划观望),别的索要在意的一点是,pop是从fromVC到toVC的历程,所以这里视图增添的逐个和push不均等,containerView先add toView然后在add fromView,确认保障fromView在最上边。

达成完push和pop动漫后将要动用到起头说的UINavigationController的代理方法了,创立一个后续自UINavigationController的子类KJNavigationController,在viewDidLoad方法中安装self.delegate = self;下一场实今世理方法

- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                            animationControllerForOperation:(UINavigationControllerOperation)operation
                                                         fromViewController:(UIViewController *)fromVC
                                                           toViewController:(UIViewController *)toVC {
    if (operation == UINavigationControllerOperationPush) {

        return [KJPushAnimator new];
    } else if (operation == UINavigationControllerOperationPop) {
        return [KJPopAnimatior new];
    }

    return nil;
}

因而operation判别是push行为依旧pop,传入对应的动画片驱动,那个时候大家就谐和模仿了系统的转场动漫,但是系统暗中同意还应该有手势左划pop回上三个页面,那就需求得以完成UINavigationController的里一个代理方法- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController

该代理再次来到的是三个落到实处了UIViewControllerInteractiveTransitioning的靶子,系统有二个已经完毕好的类UIPercentDrivenInteractiveTransition,直接使用就

图片 6

图片 7

下一场正是滑入手势的落到实处,使用UIScreenEdgePanGestureRecognizer,看名就能够知道意思,手势的触发是从显示屏的边缘(上下左右七个边缘)

- (void)setupGesture { 
    UIScreenEdgePanGestureRecognizer *edgePan = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgePanGesture:)];
    edgePan.edges = UIRectEdgeLeft;
[self.view addGestureRecognizer:edgePan];
}

安装从屏幕侧面缘触发,然后看edpePanGesuture方法

- (void)edgePanGesture:(UIScreenEdgePanGestureRecognizer *)sender {
    UIGestureRecognizerState state = sender.state;

    CGFloat offsetX = MAX(0, [sender translationInView:sender.view].x);
    CGFloat width = [UIScreen mainScreen].bounds.size.width;
    CGFloat percent = offsetX / width;
    switch (state) {
        case UIGestureRecognizerStateBegan:
        {
            self.interactive  = [[UIPercentDrivenInteractiveTransition alloc] init];
            self.interactive.completionCurve = UIViewAnimationCurveLinear;
            [self popViewControllerAnimated:YES];
        }
            break;
        case UIGestureRecognizerStateChanged:
        {

            [self.interactive updateInteractiveTransition:percent];

        }
            break;
        case UIGestureRecognizerStateEnded:
        {
            if (percent > 0.5) {
                [self.interactive finishInteractiveTransition];
            } else {
                [self.interactive cancelInteractiveTransition];
            }

            self.interactive = nil;
        }
            break;
        default:
        {
            [self.interactive cancelInteractiveTransition];
            self.interactive = nil;
        }
            break;
    }
}

第一拿到手指滑动的偏移量offsetX,总括手指滑动间隔与荧屏尺寸的比例,然后剖断手势的景观,在手势began时,调用popViewControllerAnimated方法(这一步很关键卡塔尔(قطر‎,在changed时,告诉UIPercentDrivenInteractiveTransition对象pop达成的比例,在手势end时,决断手指滑动是或不是超越显示器平时,超越日常则pop完结,调用finishInteractiveTransition,未有则表示撤废本次pop,调用cancelInteractiveTransition,而且注意将self.interactive置为nil。

上述完结那一个都感觉着能够获得push和pop时的转场进行的百分比(进度),接来下正是对navigationBar的有些布署。

给navigationBar创设二个分拣,设置navigationBar的水彩、反射率以至标题颜色

图片 8

这里不是从来对navigationBar实行那个颜色设置,而是在navigationBar上插入三个子视图,通过设置那几个子视图的颜料来呈现navigationBar的颜料

- (UIView *)backgroundView {
    UIView *backgroundView = objc_getAssociatedObject(self, _cmd);
    if (!backgroundView) {
        [self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
        backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([UIScreen mainScreen].bounds), CGRectGetHeight(self.bounds)   20)];
        backgroundView.userInteractionEnabled = NO;
        backgroundView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin|UIViewAutoresizingFlexibleTopMargin|UIViewAutoresizingFlexibleBottomMargin;
        [self.subviews.firstObject insertSubview:backgroundView atIndex:0];
        [self setBackgroundView:backgroundView];
    }
    self.backgroundColor = [UIColor clearColor];
    return backgroundView;
}
- (void)setBackgroundView:(UIView *)maskLayer {
    objc_setAssociatedObject(self, @selector(backgroundView), maskLayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

图片 9

上海教室上校_UIBarBackground的color设置为clearColor,后边的navigationBar的颜料实际上正是插入的backgroundView的颜料

末尾再给UIViewController加多一个分拣,方便直接设置navigationBar的连带属性

图片 10

图片 11

就是有的setter和getter方法,重纵然客商并未有手动设置时给定三个暗中同意置,代码比较容易就不做详细分解了,读者那个时候得以回来KJPushAnimator和KJPopAnimator中看看navigationBar的相关安装,注意动画前后navigationBar的安装。

接下来实际项目中的设置navigationBar的水彩,和标题颜色就特别轻巧了,#improt "UIViewController KJNavigationBar.h"然后在viewDidLoad中

- (void)viewDidLoad {
    [super viewDidLoad];

    self.kj_navigationBarTintColor = [UIColor cyanColor];
    self.kj_navigationTitleColor = [UIColor whiteColor];
}

实效就不啻著作伊始的gif同样

DEMO TWO: 弹性present动画

1.创造多少个调节器,手势管理类和DEMO ONE是同叁个
2.团结现实的动漫片过渡完成presentAnimationdismissAnimation多少个法子

/**
 *  presentAnimation
 */
- (void)presentAnimation:(id<UIViewControllerContextTransitioning>)transitionContent
{
    UIViewController *toVC = [transitionContent viewControllerForKey:UITransitionContextToViewControllerKey];
    UIViewController *fromVC = [transitionContent viewControllerForKey:UITransitionContextFromViewControllerKey];
    //snapshotViewAfterScreenUpdates:可以对某个试图截图,我们采用对这个截图做动画代替直接对toVC做动画,因为在手势过渡中直接使用toVC动画会和手势有冲突,如果不需要实现手势的话,就可以不是用截图了
    UIView *tempView = [fromVC.view snapshotViewAfterScreenUpdates:NO];
    tempView.frame = fromVC.view.frame;
    //因为对截图做动画,fromVC开始是隐藏的
    fromVC.view.hidden = YES;
    //containerView:如果要对视图做转场动画,视图就必须加入containerView中才能进行,可以理解为containerView管理所有做转场动画的视图
    UIView *containerView = [transitionContent containerView];
    //将截图视图和toVC的view都加入containerView中 截图视图是fromVC所以先放 先放的在下面
    [containerView addSubview:tempView];
    [containerView addSubview:toVC.view];
    //设置toVC的frame,因为是present出来不是全屏,而且在底部,如果不设置默认是整个屏幕,这里的containerView的frame就是整个屏幕
    toVC.view.frame = CGRectMake(0, containerView.height, containerView.width, 500);
    //开始动画,使用产生弹簧效果的方法
    [UIView animateWithDuration:[self transitionDuration:transitionContent] delay:0.0 usingSpringWithDamping:0.55 initialSpringVelocity:1.0 / 0.55 options:0 animations:^{
        //1.让toVC向上移动 向上是负的
        toVC.view.transform = CGAffineTransformMakeTranslation(0, -500);
        //2.让截图视图缩小即可
        tempView.transform = CGAffineTransformMakeScale(0.85, 0.85);

    } completion:^(BOOL finished) {
        //使用如下代码标记整个转场过程是否正常完成[transitionContext transitionWasCancelled]代表手势是否取消了,如果取消了就传NO表示转场失败,反之亦然,如果不是用手势的话直接传YES也是可以的,我们必须标记转场的状态,系统才知道处理转场后的操作,否者认为你一直还在,会出现无法交互的情况
        [transitionContent completeTransition:![transitionContent transitionWasCancelled]];
        //转场失败后的处理
        if ([transitionContent transitionWasCancelled])
        {
            //失败后复原动画开始是的样子
            //1.把fromVC显示出来
            fromVC.view.hidden = NO;
            //2.移除截图视图,下次会重新生成
            [tempView removeFromSuperview];
        }
    }];
}

/**
 *  dismissAnimation
 */
- (void)dismissAnimation:(id<UIViewControllerContextTransitioning>)transitionContent
{
    UIViewController *fromVC = [transitionContent viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContent viewControllerForKey:UITransitionContextToViewControllerKey];

    //参照present动画的逻辑,present成功后,containerView的第一个子视图就是截图视图,我们将其取出做动画
    UIView *containerView = [transitionContent containerView];
    UIView *tempView = containerView.subviews[0];
    //开始动画
    [UIView animateWithDuration:[self transitionDuration:transitionContent] delay:0.0 usingSpringWithDamping:0.55 initialSpringVelocity:1 / 0.55 options:0 animations:^{
        //present使用的是transform,只需要恢复就可以了
        fromVC.view.transform = CGAffineTransformIdentity;
        tempView.transform = CGAffineTransformIdentity;
    } completion:^(BOOL finished) {
        if ([transitionContent transitionWasCancelled]) {
            //标记失败
            [transitionContent completeTransition:NO];
        }else{
            //成功后,标记成功,让toVC显示出来,并移除截图视图
            [transitionContent completeTransition:YES];
            toVC.view.hidden = NO;
            [tempView removeFromSuperview];
        }
    }];
}

3.vc1中实行调用

    PEElasticOneController *elasticVC = [PEElasticOneController new];
    elasticVC.delegate = self;
    [self presentViewController:elasticVC animated:YES completion:nil];

4.vc2中监听

#pragma mark - ---UIViewControllerTransitioningDelegate---
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
    return [PEElasticTransition transitionWithTransitionType:PEElasticTransitionTypePresent];
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
    return [PEElasticTransition transitionWithTransitionType:PEElasticTransitionTypeDismiss];
}
- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id<UIViewControllerAnimatedTransitioning>)animator
{
    PEInteractiveTransition *interactivePresent = [self.delegate interactiveTransitionForPresent];
    return interactivePresent.interation ? interactivePresent : nil;
}
- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator
{
    return self.interactiveTransition.interation ? self.interactiveTransition : nil;
}

这么就着力产生了

图片 12

弹性present.gif

正文

接下去起首进入正题,本篇小说主要涉嫌到自定义转场动漫的贯彻和透过runtime给分类增加属性。

率先大家要团结完结push和pop时候的转场动漫,达成转场动画首如果达成UINavigationController的代理方法

图片 13

金口玉牙实现浅米灰框中的八个代理方法,在那之中下边包车型地铁代办方面是定义push和pop时的卡通片效果(未有手势交互作用卡塔尔(قطر‎,上面哪二个代理方法是左划荧屏pop时的卡通交互作用(push不设有手势滑动,故push不供给卡塔尔国。

上面包车型地铁代理方法重回了三个据守UIViewControllerAnimatedTransitioning协商的靶子,新建一个类KJPushAnimator并遵照该合同,该左券中有七个required方法

1、 - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
2、 - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;

率先个方法表示动漫的持续时间,第二艺术便是落实大家转场时候的卡通片了。
代码如下:

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

动漫片时间概念为0.25秒,然后另二个磋商章程的达成如下

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

    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];



    UIView *containerView = [transitionContext containerView];
    UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    toView.kj_x = [UIScreen mainScreen].bounds.size.width;

    [containerView addSubview:fromView];
    [containerView addSubview:toView];

    UINavigationBar *navigationBar = toVC.navigationController.navigationBar;

    [navigationBar kj_setBackgroundColor:fromVC.kj_navigationBarTintColor];
    [navigationBar kj_setNavigationBarAlpha:fromVC.kj_navigationBarAlpha];
    [navigationBar kj_setNavigationTitleColor:fromVC.kj_navigationTitleColor];

    [UIView animateWithDuration:kTransitionDuration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{

        fromView.kj_x = -100;
        toView.kj_x = 0;

        [navigationBar kj_setBackgroundColor:toVC.kj_navigationBarTintColor];
        [navigationBar kj_setNavigationBarAlpha:toVC.kj_navigationBarAlpha];
        [navigationBar kj_setNavigationTitleColor:toVC.kj_navigationTitleColor];

    } completion:^(BOOL finished) {
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        [navigationBar kj_setBackgroundColor:toVC.kj_navigationBarTintColor];
        [navigationBar kj_setNavigationBarAlpha:toVC.kj_navigationBarAlpha];
        [navigationBar kj_setNavigationTitleColor:toVC.kj_navigationTitleColor];
    }];
}

先是得到七个转场相关的多少个viewController(fromVC和toVC)以致它们的view(fromView和toView),这里containerView是用来承载fromView和toView的容器父视图,然后将fromView和toView add到containerView上,注意增多的顺序,注意到系统的push动漫是从右往左现身要push的vc,所以toView早先的x坐标是显示器宽度。然后是卡通最早前安设navigationBar的有关属性,因为是从fromVC push 到toVC,故navigationBar的安装先跟fromVC关联,然后简短的采用UIView动漫模仿系统的push动画,并安装navigationBar的水彩和折射率等。

福寿康宁完push转场,在来看pop转场完成:

DEMO ONE:美妙移动作效果果

1.大家先是成立2个调控器,为了有扶助笔者称做present操作的为vc1、被present的为vc2,点击一个调控器上的按键能够push出另多个操纵器.
2.开立贰个手势过渡管理的类,小编这里是PEInteractiveTransition,因为无论是哪类转场,手势调控的精气神儿都以一致的,笔者干脆就把那些手势过渡管理的类封装了一下,具体能够在.h文件之中查看,在接下去的多少个转场效果中大家都可以方便的是选拔它:
PEInteractiveTransition.h

typedef void(^GestureConfig)();

//手势转场类型
typedef NS_ENUM(NSUInteger,PEInteractiveTransitionType){
    PEInteractiveTransitionTypePresent = 0,
    PEInteractiveTransitionTypeDismiss,
    PEInteractiveTransitionTypePush,
    PEInteractiveTransitionTypePop
};

//手势方向
typedef NS_ENUM(NSUInteger,PEInteractiveTransitionGestureDirection){
    PEInteractiveTransitionGestureDirectionLeft = 0,
    PEInteractiveTransitionGestureDirectionRight,
    PEInteractiveTransitionGestureDirectionUp,
    PEInteractiveTransitionGestureDirectionDown
};

@interface PEInteractiveTransition : UIPercentDrivenInteractiveTransition
/** 记录是否开始手势,判断pop操作是手势出发还是返回键出发 */
@property (nonatomic, assign)BOOL interation;
/** 触发手势present的时候config,在config中初始化并present需要弹出的控制器 */
@property (nonatomic, copy)GestureConfig presentConfig;
/** 触发手势push的时候config,在config中初始化并push需要弹出的控制器 */
@property (nonatomic, copy)GestureConfig pushConfig;

#pragma mark - ---初始化方法---
  (instancetype)interactiveTransitionWithTransitionType:(PEInteractiveTransitionType)type GestureDirection:(PEInteractiveTransitionGestureDirection)direction;
- (instancetype)initWithTransitionType:(PEInteractiveTransitionType)type GestureDirection:(PEInteractiveTransitionGestureDirection)direction;

/** 给传入控制器添加手势*/
- (void)addPanGestureForViewController:(UIViewController *)viewController; 
@end

PEInteractiveTransition.m

#import "PEInteractiveTransition.h"

@interface PEInteractiveTransition()

/** 传入的ViewController */
@property (nonatomic, weak)UIViewController *vc;
/** 手势方向 */
@property (nonatomic, assign)PEInteractiveTransitionGestureDirection direction;
/** 手势类型 */
@property (nonatomic, assign)PEInteractiveTransitionType type;

@end
@implementation PEInteractiveTransition

  (instancetype)interactiveTransitionWithTransitionType:(PEInteractiveTransitionType)type GestureDirection:(PEInteractiveTransitionGestureDirection)direction
{
    return [[self alloc] initWithTransitionType:type GestureDirection:direction];
}

- (instancetype)initWithTransitionType:(PEInteractiveTransitionType)type GestureDirection:(PEInteractiveTransitionGestureDirection)direction{
    self = [super init];
    if (self) {
        _direction = direction;
        _type = type;
    }
    return self;
}

- (void)addPanGestureForViewController:(UIViewController *)viewController
{
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
    self.vc = viewController;
    [viewController.view addGestureRecognizer:pan];
}
/**
 *  手势过渡过程
 *
 *  @param pan 添加的手势
 */
- (void)handleGesture:(UIPanGestureRecognizer *)pan
{
    //手势百分比 初始化为0  向上和向右滑动 距离是负的 所以前面加负号 这样负负得正
    CGFloat persent = 0;
    switch (_direction) {
        case PEInteractiveTransitionGestureDirectionUp:{
            CGFloat transitionY = -[pan translationInView:pan.view].y;
            persent = transitionY / pan.view.frame.size.width;
        }
            break;
        case PEInteractiveTransitionGestureDirectionRight:{
            CGFloat transitionR = [pan translationInView:pan.view].x;
            persent = transitionR / pan.view.frame.size.width;
        }
            break;
        case PEInteractiveTransitionGestureDirectionDown:{
            CGFloat transitionD = [pan translationInView:pan.view].y;
            persent = transitionD / pan.view.frame.size.width;
        }
            break;
        case PEInteractiveTransitionGestureDirectionLeft:{
            CGFloat transitionL = -[pan translationInView:pan.view].x;
            persent = transitionL / pan.view.frame.size.width;
        }
            break;

        default:
            break;
    }
    switch (pan.state) {
        case UIGestureRecognizerStateBegan:{
            //手势开始的时候标记手势状态,并开始相应的事件
            self.interation = YES;
            [self startGesture];
        }
            break;
        case UIGestureRecognizerStateChanged:{
            //手势过程中,通过updateInteractiveTransition设置pop过程进行的百分比
            [self updateInteractiveTransition:persent];
        }
            break;
        case UIGestureRecognizerStateEnded:{
            //手势完成后结束标记并且判断移动的距离是否过半,过则finishInteractiveTransition完成转场操作,否则取消转场操作
            self.interation = NO;
            if (persent > 0.5) {
                [self finishInteractiveTransition];
            }else {
                [self cancelInteractiveTransition];
            }
        }
            break;

        default:
            break;
    }
}

- (void)startGesture
{
    switch (_type) {
        case PEInteractiveTransitionTypePresent:{
            if (_presentConfig) {
                _presentConfig();
            }
        }
            break;
        case PEInteractiveTransitionTypeDismiss:{
            [_vc dismissViewControllerAnimated:YES completion:nil];
        }
            break;
        case PEInteractiveTransitionTypePush:{
            if (_pushConfig) {
                _pushConfig();
            }
        }
            break;
        case PEInteractiveTransitionTypePop:{
            [_vc.navigationController popViewControllerAnimated:YES];
        }
            break;

        default:
            break;
    }
}

@end

3.开立玄妙效果的切实可行动漫代理
PENaviTransition.h

typedef NS_ENUM(NSUInteger, PENaviTransitionType){
    PENaviTransitionTypePush = 0,
    PENaviTransitionTypePop
};

@interface PENaviTransition : NSObject<UIViewControllerAnimatedTransitioning>


  (instancetype)transitionWithType:(PENaviTransitionType)type;
- (instancetype)initWithTransitionType:(PENaviTransitionType)type;

@end

PENaviTransition.m首要实现三个卡通时间长度方法和二个push动漫和一个pop动漫

/**
 *  动画时长
 */
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
    return 0.75;
}
/**
 *  如何执行过渡动画
 */
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
    switch (_type) {
        case PENaviTransitionTypePush:
            [self doPushAnimation:transitionContext];
            break;
        case PENaviTransitionTypePop:
            [self doPopAnimation:transitionContext];
            break;

        default:
            break;
    }
}

/**
 *  执行push过渡动画
 */
- (void)doPushAnimation:(id<UIViewControllerContextTransitioning>)transitionContext
{
    PEMagicMoveController *fromVC = (PEMagicMoveController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    PEMagicMovePushController *toVC = (PEMagicMovePushController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    //拿到当前点击的cell的imageView
    PEMagicMoveCell *cell = (PEMagicMoveCell *)[fromVC.collectionView cellForItemAtIndexPath:fromVC.clickIndex];
    UIView *containerView = [transitionContext containerView];
    UIView *tempView = [cell.imageV snapshotViewAfterScreenUpdates:NO];
    //将点击的cell截图作为临时View的内容 并将坐标系转化成push控制器种的坐标
    tempView.frame = [cell.imageV convertRect:cell.imageV.bounds toView:containerView];
    //设置动画前的各个控件的状态
    cell.imageV.hidden = YES;
    toVC.view.alpha = 0;
    toVC.imageView.hidden = YES;
    //tempView添加到containerView中保证在最前方,所以后添加
    [containerView addSubview:toVC.view];
    [containerView addSubview:tempView];
    //开始push动画
    [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 usingSpringWithDamping:0.55 initialSpringVelocity:1/0.55 options:0 animations:^{
        //临时View(也就是截取cell大小)的frame变成和push出来view种的imageView大小一样
        tempView.frame = [toVC.imageView convertRect:toVC.imageView.bounds toView:containerView];
        //push控制器由透明为0变为1
        toVC.view.alpha = 1;
    } completion:^(BOOL finished) {
        tempView.hidden = YES;
        toVC.imageView.hidden = NO;
        //如果动画过渡取消了就标记不完成,否则标记完成,这里可以直接写YES,如果有手势过渡才需要判断,必须标记,否则系统不会完成动画,会出现无法交互等bug
        [transitionContext completeTransition:YES];
    }];
}
/**
 *  执行pop过渡动画
 */
- (void)doPopAnimation:(id<UIViewControllerContextTransitioning>)transitionContext
{
    PEMagicMovePushController *fromVC = (PEMagicMovePushController *)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    PEMagicMoveController *toVC = (PEMagicMoveController *)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    PEMagicMoveCell *cell = (PEMagicMoveCell *)[toVC.collectionView cellForItemAtIndexPath:toVC.clickIndex];
    UIView *containerView = [transitionContext containerView];
    //这里的lastObject就是push适合初始化的那个tempView
    UIView *tempView = containerView.subviews.lastObject;
    //设置动画前状态
    cell.imageV.hidden = YES;       //列表页面的cell先隐藏
    fromVC.imageView.hidden = YES;  //当前页面的imageView也进行隐藏
    tempView.hidden = NO;           //最上面的临时View显示
    [containerView insertSubview:toVC.view atIndex:0];
    //开始pop动画
    [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0.0 usingSpringWithDamping:.55 initialSpringVelocity:1/0.55 options:0 animations:^{
        //将临时View的坐标和大小变成新坐标系中列表cell的坐标和大小
        tempView.frame = [cell.imageV convertRect:cell.imageV.bounds toView:containerView];
        //同时详情页面隐藏
        fromVC.view.alpha = 0;
    } completion:^(BOOL finished) {
        //加入了手势判断
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
        if ([transitionContext transitionWasCancelled]) {
            //手势取消了,原来隐藏的imageView要显示出来
            tempView.hidden = YES;
            fromVC.imageView.hidden = NO;
        }else{
            //手势成功
            cell.imageV.hidden = NO;        //列表点击的cell要显示
            [tempView removeFromSuperview]; //临时的要去除 因为下一次会重新生成 不会产生冗余
        }
    }];
}

4.现行能够在切实举办跳转的页面vc1开展利用

#pragma mark <UICollectionViewDelegate>
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    //记录点击的是列表的第几个 这样在返回动画的时候能用上
    _clickIndex = indexPath;
    PEMagicMovePushController *vc = [[PEMagicMovePushController alloc] init];
    //设置导航控制器的代理为推出的控制器,可以达到自定义不同控制器推出效果的目的
    self.navigationController.delegate = vc;
    [self.navigationController pushViewController:vc animated:YES];
}

5.在push出的页面vc2实行连接动画的监听和完结

//返回手势过渡管理对象
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
    //分pop和push两种情况分别返回动画过渡代理相应不同的动画操作
    return [PENaviTransition transitionWithType:operation == UINavigationControllerOperationPush ? PENaviTransitionTypePush : PENaviTransitionTypePop];
}

//返回转场动画过渡管理对象
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController
{
    //如果是手动出发则返回我们的UIPercentDrivenInteractiveTransition对象
    return _interactiveTransition.interation ? _interactiveTransition : nil;
}

6.完了,就是上边包车型客车效应

图片 14

奇妙效果.gif

本文由68399皇家赌场发布于呼叫中心培训课程,转载请注明出处:自定义转场动画实现导航栏渐变效果,iOS自定义

关键词: 68399皇家赌场 日记本 自定义 动画 转场

最火资讯