简介

现在越来越多的应用有看大图或者进入详情页面,但是,再返回的时候,普通意义上,会点击左上角的返回,这时候你就会发现,还需要将手指移动到左上角,这样,无意给用户增添了麻烦,并且,现在手机屏幕越来越大,这样返回的越来越困难,在体验上特别的差劲.

尽管苹果推出了从左边缘右滑返回,FDFullscreenPopGesture这个很强大的,全屏右滑返回…..尽管现在考虑到用户体验上,已经有了很大的提升,但是,仍然,在大屏上,不是很好操作…因为,你在正常使用手机的时候,大拇指使用的频率要远远大于其他手指,而且,大拇指,上下滑动的体验度是要大于左右滑动的体验度的…..所以,这里就有着下滑返回的需求必要的…

开始

解决完需求的原因,下面我们来看看如何做?

1.转场动画的设置

1. 遵循协议

需要转场的ViewCointroller遵循:UIViewControllerTransitioningDelegate

2. 转场代理设置

// 设置Presented的动画
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {return [[PresentVCAnimation alloc] init];
}/// 设置Dismiss返回的动画设置
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {return [[DismissVCAnimation alloc] init];
}/// 设置过场动画
- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator {return (self.interactiveTransition.isInteracting ? self.interactiveTransition : nil);
}

2. PresentVCAnimation

PresentVCAnimation.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface PresentVCAnimation : NSObject<UIViewControllerAnimatedTransitioning>@end

PresentVCAnimation.m

#import "PresentVCAnimation.h"@implementation PresentVCAnimation
// 设置动画的时间长度
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {return 0.25;
}-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {// 前一个ViewController,动画的发起者UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];// 后一个ViewController,动画的结束者UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];// 转场动画的最终的frameCGRect finalFrameForVC = [transitionContext finalFrameForViewController:toVC];// 下面敲黑板啦// 转换的容器view,这里是存放转场动画的容器UIView *containerView = [transitionContext containerView];// 这里一般情况下,没有涉及到VC的View放大或者缩小,即可看做是屏幕的尺寸CGRect bounds = [UIScreen mainScreen].bounds;// 这是后一个ViewController的frametoVC.view.frame = CGRectOffset(finalFrameForVC, 0, bounds.size.height);[containerView addSubview:toVC.view];// 下面是改变前一个ViewController和后一个ViewController的动画[UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{fromVC.view.alpha = 0.5;toVC.view.frame = finalFrameForVC;} completion:^(BOOL finished) {[transitionContext completeTransition:YES];fromVC.view.alpha = 1.0;}];
}-(void)animationEnded:(BOOL)transitionCompleted {
}

3.DismissVCAnimation

DismissVCAnimation.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface DismissVCAnimation : NSObject<UIViewControllerAnimatedTransitioning>@end

DismissVCAnimation.m

#import "DismissVCAnimation.h"@implementation DismissVCAnimation
-(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {return 0.25;
}-(void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {// 前一个ViewController,动画的发起者UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];// 后一个ViewController,动画的结束者UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];CGRect screenBounds = [UIScreen mainScreen].bounds;// 获取前一个页面的frameCGRect initFrame = [transitionContext initialFrameForViewController:fromVC];// 转场动画的toView的最终的frameCGRect finalFrame = CGRectOffset(initFrame, 0, screenBounds.size.height);// 转换的容器viewUIView *containerView = [transitionContext containerView];// 下面这里是为了让转场动画衔接的更和谐,不然,下滑一点距离就直接看到之前页面的内容,体验不好UIView *bgView = [[UIView alloc] initWithFrame:fromVC.view.bounds];bgView.backgroundColor = [UIColor blackColor];[toVC.view addSubview:bgView ];[containerView addSubview:toVC.view];[containerView sendSubviewToBack:toVC.view];[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{fromVC.view.frame = finalFrame;bgView.alpha = 0;} completion:^(BOOL finished) {[bgView removeFromSuperview];BOOL complate = [transitionContext transitionWasCancelled];[transitionContext completeTransition:(!complate)];}];
}-(void)animationEnded:(BOOL)transitionCompleted {
}
@end

4.SwipeUpInteractiveTransition

这个是针对滑动手势对转场动画的影响的类.

这里面主要处理了滑动行为的状态,需要重点了解的是:
1.判断用户是不是存在返回意图的行为,这里面规定了两种行为,而这两种行为,均为在当前页面手势结束的时候处理

返回意图的行为的判断条件:

  • 当下滑距离大于当前屏幕比例的 0.382 (无耻的取了黄金比例的对半,因为,感觉有逼格,可进行相应调整)
  • 快速滑动行为,也就是滑动速度 : iOS开发–手势滑动的速度

SwipeUpInteractiveTransition.h

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>@interface SwipeUpInteractiveTransition : UIPercentDrivenInteractiveTransition
/// 手势中...
@property (nonatomic, assign) BOOL isInteracting;
/// 完成动画
@property (nonatomic, assign) BOOL shouldComplete;
// 初始化
- (instancetype)initWithGestureViewController:(UIViewController *)gestureVC;
@end

SwipeUpInteractiveTransition.m

#define KEY_WINDOW  [[UIApplication sharedApplication].delegate window]#import "SwipeUpInteractiveTransition.h"@interface SwipeUpInteractiveTransition()
// 手势添加的View
@property (nonatomic, strong) UIViewController *gestureVC;
// 记录手势结束前的点击位置
@property (nonatomic, assign) CGPoint oldTranslation;
// 是不是需要返回,这里是需要猜想并判断用户是不是存在返回行为
@property (nonatomic, assign) BOOL isNeedDismiss;
@end@implementation SwipeUpInteractiveTransition
- (instancetype)initWithGestureViewController:(UIViewController *)gestureVC
{self = [super init];if (self) {_gestureVC = gestureVC;// 添加手势UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panGestureHandler:)];[_gestureVC.view addGestureRecognizer:pan];}return self;
}- (void)panGestureHandler:(UIPanGestureRecognizer *)gesture {// 获取手势触控的点在作用View的相对位置CGPoint translation = [gesture translationInView:gesture.view];// 每次手势触发的时候,重置,也就是用户不存在返回意图self.isNeedDismiss = NO;switch (gesture.state) {case UIGestureRecognizerStateBegan: {// 手势开始// 交互动画判断_isInteracting = YES;[_gestureVC dismissViewControllerAnimated:YES completion:nil];break;}case UIGestureRecognizerStateChanged: {// 当前触控点的赋值self.oldTranslation = translation;// 触控点的转化,因为updateInteractiveTransition的参数范围是[0.1],所以这里需要左边比例的转换CGFloat fraction = (translation.y / KEY_WINDOW.bounds.size.height);// 保证范围fraction = fmin(fmaxf(fraction, 0.0), 1.0);// 这里进行滑动中的判断,取的是黄金比例,也就是,如果滑动距离占比约38%,即可判断用户存在返回的行为_shouldComplete = fraction > 0.382;// 更新进度[self updateInteractiveTransition:fraction];break;}case UIGestureRecognizerStateEnded: {// 这里是重要的判断// 如果用户存在快速向下滑动的行为(等同于全屏快速向右滑动返回的行为),self.isNeedDismiss为YES// 而这个判断的快速范围如下 CGPoint speed = [gesture velocityInView:gesture.view];// 这个数据经过了大量测试和舒适度的数据,大家可以参考,具体的还需要根据实际情况而定.可以看我的量if (speed.y >= 920) {self.isNeedDismiss = YES;_shouldComplete = YES;}// 手势交互结束_isInteracting = NO;// 下面进行之前判断的处理// 使用dispatch_source_t定时回调,进行改变self.oldTranslation的数据,进行模拟手势移动,因为如果直接调用[self cancelInteractiveTransition];或者[self updateInteractiveTransition:fraction];就会发现,动画瞬间变化,瞬间复位或者瞬间消失返回.考虑到用户体验,如下dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//定时器模式  事件源dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, quene);//NSEC_PER_SEC是秒,*1是每秒dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), NSEC_PER_SEC * 0.0001, 0);//设置响应dispatch源事件的block,在dispatch源指定的队列上运行dispatch_source_set_event_handler(timer, ^{//回调主线程,在主线程中操作UIdispatch_async(dispatch_get_main_queue(), ^{if (!_isNeedDismiss && (!_shouldComplete || gesture.state == UIGestureRecognizerStateCancelled)) {if (self.oldTranslation.y <= 0) {// 当满足条件,执行取消动画[self cancelInteractiveTransition];// 移除时间回调dispatch_source_cancel(timer);} else {// 模拟上滑行为CGFloat fraction = (self.oldTranslation.y / KEY_WINDOW.bounds.size.height);fraction = fmin(fmaxf(fraction, 0.0), 1.0);self.oldTranslation = CGPointMake(self.oldTranslation.x, self.oldTranslation.y - 0.3);[self updateInteractiveTransition:fraction];}} else {if (self.oldTranslation.y > KEY_WINDOW.bounds.size.height) {// 当滑出当前屏幕,执行完成动画[self finishInteractiveTransition];// 移除时间回调dispatch_source_cancel(timer);} else {// 模拟下滑行为CGFloat fraction = (self.oldTranslation.y / KEY_WINDOW.bounds.size.height);fraction = fmin(fmaxf(fraction, 0.0), 1.0);self.oldTranslation = CGPointMake(self.oldTranslation.x, self.oldTranslation.y + 0.3);[self updateInteractiveTransition:fraction];}}});});//启动源dispatch_resume(timer);break;}default:break;}
}
@end

iOS开发--下滑返回dismiss相关推荐

  1. 服务器返回文件格式,iOS开发--服务器返回的数据解析

    App要与服务器交互才能达到数据更新和获取资源 那么: 服务器返回客户端的数据,一般返回两种格式:JSON格式.XML格式 (文件下载除外) 什么是JSON 轻量级数据格式,一般用于数据交互 JSON ...

  2. 【ios开发/Xcode】实现登录注册

    [ios开发/Xcode]实现登录注册 实现效果 源代码 实现效果 首先进入初始界面,输入账号Linchuantao,密码Linchuantao,显示登录失败(如下左图),因此需要进行注册,点击左下角 ...

  3. iOS开发UI篇—Modal简单介绍

    iOS开发UI篇-Modal简单介绍 一.简单介绍 除了push之外,还有另外一种控制器的切换方式,那就是Modal 任何控制器都能通过Modal的形式展⽰出来 Modal的默认效果:新控制器从屏幕的 ...

  4. iOS开发UINavigation系列四——导航控制器UINavigationController

    iOS开发UINavigation系列四--导航控制器UINavigationController 一.引言 在前面的博客中,我么你介绍了UINavigationBar,UINavigationIte ...

  5. iOS 开发之内购 – AppStore

    来源:Yi'mouleng(@丶伊眸冷) 链接:http://t.cn/R4L0rgA 前言 本文会给大家详细介绍iOS内购,虽然之前网上也有内购的教程,但是还不够详细,我重新整理出一份教程,希望对大 ...

  6. iOS开发--APP性能检测方案汇总(一)

    Linux编程点击右侧关注,免费入门到精通! 作者丨青苹果园 https://github.com/SilongLi 1 . CPU 占用率 CPU作为手机的中央处理器,可以说是手机最关键的组成部分, ...

  7. IOS开发之内购-AppStore

    iOS开发之内购-AppStore AppStore 内购 支付 iOS开发 前言 本文会给大家详细介绍iOS内购,虽然之前网上也有内购的教程,但是还不够详细,我重新整理出一份教程,希望对大家有所帮助 ...

  8. iOS开发常用三方库、插件、知名博客

    TimLiu-iOS iOS开发常用三方库.插件.知名博客等等,期待大家和我们一起共同维护,同时也期望大家随时能提出宝贵的意见(直接提交Issues即可). 持续更新... 版本:Objective- ...

  9. iOS开发系列--通讯录、蓝牙、内购、GameCenter、iCloud、Passbook系统服务开发汇总...

    iOS开发过程中有时候难免会使用iOS内置的一些应用软件和服务,例如QQ通讯录.微信电话本会使用iOS的通讯录,一些第三方软件会在应用内发送短信等.今天将和大家一起学习如何使用系统应用.使用系统服务: ...

最新文章

  1. IDC公司:服务器类微处理器市场最新预测
  2. 剑与远征种族刻印让玩家期待,绿裔刻印真有那么好看
  3. Mysql InnoDB Plugin安装 install
  4. 两教授吐槽:如今博士研究生的论文写作水平为何如此堪忧?
  5. Github | Google开源高性能机器学习研究工具Jax
  6. git分支开发常用命令
  7. resultMap和resultType的整体的区别和联系
  8. 宏基 4560G笔记本 AMD APU A6-3400试用报告
  9. 【网络传输协议】RTSP即RealTimeStreamingProtocol流媒体网络传输协议
  10. 移动化之后,BAT下一步走向何方?我们又该走向何方?
  11. pytorch个人学习笔记(2)—Normalize()参数详解及用法
  12. shareSDK导入的常见错误及解决方法
  13. ESP8266多任务处理---Ticker库
  14. 第一章 计算机系统基础知识(1)
  15. 神犇营-my1002-朋友圈集赞
  16. PHP项目开发案例全程实录pdf
  17. 雅虎說【用戶將無法從中國大陸使用 Yahoo 的產品與服務】电话号码选项中将(+86)删除
  18. 苹果新品“翻车”后,官方回应来了!
  19. c语言课程设计 水电费,广东工业大学水电费系统C语言设计(文件操作部分)
  20. 二进制流转PDF,未能加载的pdf文档

热门文章

  1. 2019-06-04 9个接私活的网站,你有码,我有钱
  2. 浏览器代理服务器拒绝连接
  3. 2018/05/11,暗里着迷
  4. sipp 注册脚本测试服务端含(401)注册流程(UAC )
  5. 服务器启动项目抛错 没有到主机的路由
  6. 网页dom元素过多为什么会导致页面卡顿
  7. 解析ip到对应城市:ipdatabase
  8. (读书笔记) 暗时间 (2016.12.17更)
  9. java进程和ksoftirqd进程的CPU突然涨高的原因
  10. 华硕Eee PC 设置U盘启动