iOS动画效果八:实现类似系统的测滑返回效果

这篇文章主要介绍怎样实现类似系统的测滑返回效果
最终的效果图为:


自定义测滑返回手势.gif

Demo地址为:
Demo地址

对应的实现文件为:
EightViewController、CustomPopGestureViewController、PopAnimation

关于转场动画,下面这张图将的很清楚


转场动画的相关类和协议的方法

为了实现类似系统的测滑返回效果,实际上就是pop的自定义返回动画,再加上UIPanGestureRecognizer手势的交互实现,也就是交互性转场动画(因为根据手指滑动的距离,大于一半会pop到上一个浏览器,小于一半会恢复原状)

由上面的图可以看到,想要实现交互性转场动画,需要实现UIViewControllerInteractiveTransitioning,
但是系统有给我们提供一个可交互的百分比动画实现类,即UIPerCententDrivenInteractiveTransition,
在接下来的实现中,我们可以直接使用系统提供的实现类,也可以自定义一个遵守UIViewControllerInteractiveTransitioning协议的类(我们是使用了系统提供的)

首先,我们先把pop动画的自定义给完成
在eightViewController中,搭建基本的界面,可以push到CustomPopGestureViewController中
在eightViewController.h中

@property (nonatomic, strong) UIButton *button;

在eightViewController.m中

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"测滑返回手势测试";
    self.navigationController.navigationBar.translucent = NO;
    self.view.backgroundColor = [UIColor whiteColor];
    
    self.button.center = self.view.center;
    [self.view addSubview:self.button];
}

- (UIButton *)button {
    if (_button) {
        return _button;
    }
    _button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
    [_button setTitle:@"跳转下一个播放器" forState:UIControlStateNormal];
    [_button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [_button addTarget:self action:@selector(buttonClicked) forControlEvents:UIControlEventTouchUpInside];
    return _button;
}

- (void)buttonClicked {
    NSLog(@"点击了跳转按钮");
    CustomPopGestureViewController *vc = [[CustomPopGestureViewController alloc] init];
    [self.navigationController pushViewController:vc animated:YES];
}

点击按钮,可以push到CustomGestureViewcontroller中

为了实现自定义的测滑返回功能,我们需要禁用系统的测滑返回功能
在CustomViewController中,我们需要添加以下代码禁用

 //禁用系统自带的侧滑返回手势
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;

接下来,先实现pop的自定义动画
在CustomViewController中,需要遵守UINavigationDelegate协议

self.navigationController.delegate = self;

实现相应的协议方法

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
    if (operation == UINavigationControllerOperationPop) {
        NSLog(@"执行了这个方法PopAnimation");
      //popAnimation是我们后面定义的类,用来实现具体的动画效果
        return [[PopAnimation alloc] init];
    }
    return nil;
}

接下来,我们自定义一个类PopAnimation,遵守协议UIViewControllerAnimatedTransitioning
在类中实现相应的协议方法

//设置动画执行的时常
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
    return 1.0f;
}

//用来处理具体的动画
- (void)animateTransition:(nonnull id<UIViewControllerContextTransitioning>)transitionContext {
    UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    //需要将转场后的界面给加上去才行
//    [[transitionContext containerView] addSubview:toVC.view];
    [[transitionContext containerView] insertSubview:toVC.view belowSubview:fromVC.view];
    NSTimeInterval duration = [self transitionDuration:transitionContext];
    CGFloat width = [UIScreen mainScreen].bounds.size.width;
    CGFloat height = [UIScreen mainScreen].bounds.size.height;
    [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
        fromVC.view.frame = CGRectMake(width, 0, width, height);
    } completion:^(BOOL finished) {
        [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
    }];
}

上面实现动画的思路:

我们可以从最终效果图上可以看成:
pop动画的过程就是将上面的view向右挪动,直到移出界面
所以代码中有这一句
[[transitionContext containerView] insertSubview:toVC.view belowSubview:fromVC.view];
将toView的view放在fromVC.view的下面,使用UIview animation来完成移动动画

注意:这里只能使用UIView animation来完成动画,代码中注释的是我最初使用CABasicAnimation来完成移动动画,pop自定义动画可行,但是对于后面使用系统类UIPerCententDrivenInteractiveTransition完成交互不可行,
如果想要使用CaBasicAnimation,必须自定义可交互的百分比动画类

这样我们就完成了pop的自定义动画返回,但是我们需要加入交互,通过判断手指滑动的距离来判断动画的进行程度

接下来,我们需要在CustomPopGestureViewController中,定义相应的手势属性和相应的交互类

@property (nonatomic, strong) UIPanGestureRecognizer *pan;
@property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactiveTransition;

接下来完成相应的手势类的添加

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.title = @"类似系统侧滑返回手势实现";
    self.navigationController.navigationBar.translucent = NO;
    self.view.backgroundColor = [UIColor yellowColor];
    
    //禁用系统自带的侧滑返回手势
    self.navigationController.interactivePopGestureRecognizer.enabled = NO;
    
    [self.view addGestureRecognizer:self.pan];
    
    self.navigationController.delegate = self;
}

- (UIPanGestureRecognizer *)pan {
    if (_pan) {
        return _pan;
    }
    _pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    return _pan;
}

在手势类的出发方法中,我们需要根据UIPanGestureRecognizer的状态来初始化或者更新或者取消或者结束交互类

UIGestureRecognizerStateBegan:手势类刚开始出发时,初始化相应的interactiveTransition
UIGestureRecognizerStateChanged:手势继续滑动的时候,交互类进行相应的更新
UIGestureRecognizerStateEnded:手势结束时,交互类结束
UIGestureRecognizerStateCancelled:手势取消时,交互类取消
最后一个手势周期完成后都要将交互类interactiveTransition置为nil

代码为:

- (void)pan:(UIPanGestureRecognizer *)pan {
    if (self.navigationController.childViewControllers.count == 1) {
        return;
    }
    CGPoint point = [pan translationInView:self.view];
    CGFloat percent = point.x / self.view.bounds.size.width;
    NSLog(@"percent的进度值为:%f",percent);
    percent = MIN(MAX(0, percent), 1);
    if (pan.state == UIGestureRecognizerStateBegan) {
        _interactiveTransition = [[UIPercentDrivenInteractiveTransition alloc] init];
        [self.navigationController popViewControllerAnimated:YES];
    } else if (pan.state == UIGestureRecognizerStateChanged) {
        [_interactiveTransition updateInteractiveTransition:percent];
    } else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) {
        //手势结束后,若进度大于0.5就完成pop动画,否则取消
        if (percent > 0.5) {
            [_interactiveTransition finishInteractiveTransition];
        } else {
            [_interactiveTransition cancelInteractiveTransition];
        }
        _interactiveTransition = nil;
    }
}

另外,不要忘记,在UINavigationDelegate协议方法中实现可交互类的协议方法

- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
    if ([animationController isKindOfClass:[PopAnimation class]]) {
        NSLog(@"执行了这个方法UIPercentDrivenInteractiveTransition");
        return _interactiveTransition;
    }
    return nil;
}

最终的效果图为:


自定义测滑返回手势.gif

总结

最终效果图为:


自定义测滑返回手势.gif

Demo地址为:
Demo地址

iOS开发中动画效果的探究(一)

iOS动画效果的探究二:UIView Animation实现动画

iOS动画效果三:CABAsicAnimation实现平移、旋转和放大

ios动画效果四:使用Pop框架实现弹簧效果

iOS动画效果五:CABasicAnimation实现绕定点旋转的效果]

iOS动画效果六:实现自定义的push转场动画

iOS动画效果七:实现自定义present转场动画效果

iOS动画效果八:实现类似系统的测滑返回效果

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