@上一章,介绍了主要的iOS7所增加的API,可以发现,它们不是一个个死的方法,苹果给我们开发者提供的是都是协议接口,所以我们能够很好的单独提出来写成一个个类,在里面实现我们各种自定义效果.

       1.先来看看实现UIViewControllerAnimatedTransitioning的自定义动画类

/***  自定义的动画类*  实现协议------>@protocol UIViewControllerAnimatedTransitioning*  这个接口负责切换的具体内容,也即“切换中应该发生什么”*/
@interface MTHCustomAnimator : NSObject <UIViewControllerAnimatedTransitioning>@end@implementation MTHCustomAnimator// 系统给出一个切换上下文,我们根据上下文环境返回这个切换所需要的花费时间
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{return 1.0;
}// 完成容器转场动画的主要方法,我们对于切换时的UIView的设置和动画都在这个方法中完成
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{// 可以看做为destination ViewControllerUIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];// 可以看做为source ViewControllerUIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];// 添加toView到容器上// 如果是XCode6 就可以用这段if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0){// iOS8 SDK 新APIUIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];//UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];[[transitionContext containerView] addSubview:toView];}else{// 添加toView到容器上[[transitionContext containerView] addSubview:toViewController.view];}// 如果是XCode5 就是用这段[[transitionContext containerView] addSubview:toViewController.view];toViewController.view.alpha = 0.0;[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{// 动画效果有很多,这里就展示个左偏移fromViewController.view.transform = CGAffineTransformMakeTranslation(-320, 0);toViewController.view.alpha = 1.0;} completion:^(BOOL finished) {fromViewController.view.transform = CGAffineTransformIdentity;// 声明过渡结束-->记住,一定别忘了在过渡结束时调用 completeTransition: 这个方法[transitionContext completeTransition:![transitionContext transitionWasCancelled]];}];
}

PS:从协议中两个方法可以看出,上面两个必须实现的方法需要一个转场上下文参数,这是一个遵从UIViewControllerContextTransitioning 协议的对象。通常情况下,当我们使用系统的类时,系统框架为我们提供的转场代理(Transitioning Delegates),为我们创建了转场上下文对象,并把它传递给动画控制器。

// MainViewController
@interface MTHMainViewController () <UINavigationControllerDelegate,UIViewControllerTransitioningDelegate>@property (nonatomic,strong) MTHCustomAnimator *customAnimator;
@property (nonatomic,strong) PDTransitionAnimator *minToMaxAnimator;
@property (nonatomic,strong) MTHNextViewController *nextVC;
// 交互控制器 (Interaction Controllers) 通过遵从 UIViewControllerInteractiveTransitioning 协议来控制可交互式的转场。
@property (strong, nonatomic) UIPercentDrivenInteractiveTransition* interactionController;
@end@implementation MTHMainViewController- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];if (self) {// Custom initialization}return self;
}- (void)viewDidLoad
{[super viewDidLoad];// Do any additional setup after loading the view.self.navigationItem.title = @"Demo";self.view.backgroundColor = [UIColor yellowColor];// 设置代理self.navigationController.delegate = self;// 设置转场动画self.customAnimator = [[MTHCustomAnimator alloc] init];self.minToMaxAnimator = [PDTransitionAnimator new];self.nextVC = [[MTHNextViewController alloc] init];// Present的代理和自定义设置_nextVC.transitioningDelegate = self;_nextVC.modalPresentationStyle = UIModalPresentationCustom; (貌似有BUG)换成modalTransitionStyle = UIModalPresentationCustom// PushUIButton *pushButton = [UIButton buttonWithType:UIButtonTypeSystem];pushButton.frame = CGRectMake(140, 200, 40, 40);[pushButton setTitle:@"Push" forState:UIControlStateNormal];[pushButton addTarget:self action:@selector(push) forControlEvents:UIControlEventTouchUpInside];[self.view addSubview:pushButton];// PresentUIButton *modalButton = [UIButton buttonWithType:UIButtonTypeSystem];modalButton.frame = CGRectMake(265, 500, 50, 50);[modalButton setTitle:@"Modal" forState:UIControlStateNormal];[modalButton addTarget:self action:@selector(modal) forControlEvents:UIControlEventTouchUpInside];[self.view addSubview:modalButton];// 实现交互操作的手势UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didClickPanGestureRecognizer:)];[self.navigationController.view addGestureRecognizer:panRecognizer];
}- (void)push
{[self.navigationController pushViewController:_nextVC animated:YES];
}- (void)modal
{[self presentViewController:_nextVC animated:YES completion:nil];
}#pragma mark - UINavigationControllerDelegate iOS7新增的2个方法
// 动画特效
- (id<UIViewControllerAnimatedTransitioning>) navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{/***  typedef NS_ENUM(NSInteger, UINavigationControllerOperation) {*     UINavigationControllerOperationNone,*     UINavigationControllerOperationPush,*     UINavigationControllerOperationPop,*  };*/if (operation == UINavigationControllerOperationPush) {return self.customAnimator;}else{return nil;}
}// 交互
- (id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController*)navigationController                           interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>)animationController
{/***  在非交互式动画效果中,该方法返回 nil*  交互式转场,自我理解意思是,用户能通过自己的动作来(常见:手势)控制,不同于系统缺省给定的push或者pop(非交互式)*/return _interactionController;
}#pragma mark - Transitioning Delegate (Modal)
// 前2个用于动画
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{self.minToMaxAnimator.animationType = AnimationTypePresent;return _minToMaxAnimator;
}-(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{self.minToMaxAnimator.animationType = AnimationTypeDismiss;return _minToMaxAnimator;
}// 后2个用于交互
- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator
{return _interactionController;
}- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator
{return nil;
}

@以上实现的是非交互的转场,指的是完全按照系统指定的切换机制,用户无法中途取消或者控制进度切换.那怎么来实现交互转场呢:

UIPercentDrivenInteractiveTransition实现了UIViewControllerInteractiveTransitioning接口的类,,可以用一个百分比来控制交互式切换的过程。我们在手势识别中只需要告诉这个类的实例当前的状态百分比如何,系统便根据这个百分比和我们之前设定的迁移方式为我们计算当前应该的UI渲染,十分方便。具体的几个重要方法:
-(void)updateInteractiveTransition:(CGFloat)percentComplete 更新百分比,一般通过手势识别的长度之类的来计算一个值,然后进行更新。之后的例子里会看到详细的用法
-(void)cancelInteractiveTransition 报告交互取消,返回切换前的状态
–(void)finishInteractiveTransition 报告交互完成,更新到切换后的状态

#pragma mark - 手势交互的主要实现--->UIPercentDrivenInteractiveTransition
- (void)didClickPanGestureRecognizer:(UIPanGestureRecognizer*)recognizer
{UIView* view = self.view;if (recognizer.state == UIGestureRecognizerStateBegan) {// 获取手势的触摸点坐标CGPoint location = [recognizer locationInView:view];// 判断,用户从右半边滑动的时候,推出下一个VC(根据实际需要是推进还是推出)if (location.x > CGRectGetMidX(view.bounds) && self.navigationController.viewControllers.count == 1){self.interactionController = [[UIPercentDrivenInteractiveTransition alloc] init];//[self presentViewController:_nextVC animated:YES completion:nil];}} else if (recognizer.state == UIGestureRecognizerStateChanged) {// 获取手势在视图上偏移的坐标CGPoint translation = [recognizer translationInView:view];// 根据手指拖动的距离计算一个百分比,切换的动画效果也随着这个百分比来走CGFloat distance = fabs(translation.x / CGRectGetWidth(view.bounds));// 交互控制器控制动画的进度[self.interactionController updateInteractiveTransition:distance];} else if (recognizer.state == UIGestureRecognizerStateEnded) {CGPoint translation = [recognizer translationInView:view];// 根据手指拖动的距离计算一个百分比,切换的动画效果也随着这个百分比来走CGFloat distance = fabs(translation.x / CGRectGetWidth(view.bounds));// 移动超过一半就强制完成if (distance > 0.5) {[self.interactionController finishInteractiveTransition];} else {[self.interactionController cancelInteractiveTransition];}// 结束后一定要置为nilself.interactionController = nil;}
}

       @最后,给大家分享一个动画特效:类似于飞兔云传的发送ViewController切换

@implementation PDTransitionAnimator#define Switch_Time 1.2
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {return Switch_Time;
}#define Button_Width 50.f
#define Button_Space 10.f
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];UIView * toView = toViewController.view;UIView * fromView = fromViewController.view;if (self.animationType == AnimationTypeDismiss) {// 这个方法能够高效的将当前显示的view截取成一个新的view.你可以用这个截取的view用来显示.例如,也许你只想用一张截图来做动画,毕竟用原始的view做动画代价太高.因为是截取了已经存在的内容,这个方法只能反应出这个被截取的view当前的状态信息,而不能反应这个被截取的view以后要显示的信息.然而,不管怎么样,调用这个方法都会比将view做成截图来加载效率更高.UIView * snap = [toView snapshotViewAfterScreenUpdates:YES];[transitionContext.containerView addSubview:snap];[snap setFrame:CGRectMake([UIScreen mainScreen].bounds.size.width - Button_Width - Button_Space, [UIScreen mainScreen].bounds.size.height - Button_Width - Button_Space, Button_Width, Button_Width)];[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{[snap setFrame:[UIScreen mainScreen].bounds];} completion:^(BOOL finished) {[UIView animateWithDuration:0.5 animations:^{[[transitionContext containerView] addSubview:toView];snap.alpha = 0;} completion:^(BOOL finished) {[snap removeFromSuperview];[transitionContext completeTransition:![transitionContext transitionWasCancelled]];}];}];} else {UIView * snap2 = [toView snapshotViewAfterScreenUpdates:YES];[transitionContext.containerView addSubview:snap2];UIView * snap = [fromView snapshotViewAfterScreenUpdates:YES];[transitionContext.containerView addSubview:snap];[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{[snap setFrame:CGRectMake([UIScreen mainScreen].bounds.size.width - Button_Width - Button_Space+ (Button_Width/2), [UIScreen mainScreen].bounds.size.height - Button_Width - Button_Space + (Button_Width/2), 0, 0)];} completion:^(BOOL finished) {[UIView animateWithDuration:0.5 animations:^{//snap.alpha = 0;} completion:^(BOOL finished) {[snap removeFromSuperview];[snap2 removeFromSuperview];[[transitionContext containerView] addSubview:toView];// 切记不要忘记了噢[transitionContext completeTransition:![transitionContext transitionWasCancelled]];}];}];}
}

@其中,snapshotViewAfterScreenUpdates 方法的解释,我也不是很懂,反正初级来说会用就行,还可以参照下面的解析:

在iOS7 以前, 获取一个UIView的快照有以下步骤: 首先创建一个UIGraphics的图像上下文,然后将视图的layer渲染到该上下文中,从而取得一个图像,最后关闭图像上下文,并将图像显示在UIImageView中。现在我们只需要一行代码就可以完成上述步骤了:

[view snapshotViewAfterScreenUpdates:NO]; 
这个方法制作了一个UIView的副本,如果我们希望视图在执行动画之前保存现在的外观,以备之后使用(动画中视图可能会被子视图遮盖或者发生其他一些变化),该方法就特别方便。
afterUpdates参数表示是否在所有效果应用在视图上了以后再获取快照。例如,如果该参数为NO,则立马获取该视图现在状态的快照,反之,以下代码只能得到一个空白快照:
[view snapshotViewAfterScreenUpdates:YES]; 
[view setAlpha:0.0]; 
由于我们设置afterUpdates参数为YES,而视图的透明度值被设置成了0,所以方法将在该设置应用在视图上了之后才进行快照,于是乎屏幕空空如也。另外就是……你可以对快照再进行快照……继续快照……

      最后,主要代码已经给出,我已经上传完整代码了,大家有意向的可以去下载下来看看(唉,主要是CSDN不支持gif动态图,好蛋疼,朋友在博客园的博客都支持,但是我又不喜欢博客园的风格,看来以后自己弄个域名博客是王道,后续我也会把我的一些代码分享到github上)
      @转载请注明,转自iOS@迷糊小书童
      @待续.....下一章给出自定义ViewController容器的转场.

iOS7新特性 ViewController转场切换(二) 系统视图控制器容器的切换动画---push pop present dismis相关推荐

  1. 跟我学 Java 8 新特性之 Stream 流(二)关键知识点

    转载自   跟我学 Java 8 新特性之 Stream 流(二)关键知识点 我们的第一篇文章,主要是通过一个Demo,让大家体验了一下使用流API的那种酣畅淋漓的感觉.如果你没有实践,我还是再次呼吁 ...

  2. Oracle 11g新特性direct path read引发的系统停运故障诊断处理

    Oracle 11g新特性direct path read引发的系统停运故障诊断处理 黎俊杰 | 2016-07-28 14:37 声明:部分表名为了脱敏而用XX代替 1.故障现象 (1)一个业务系统 ...

  3. react 数组新增_React 新特性 Hooks 讲解及实例(二)

    本文是 React 新特性系列的第二篇,第一篇请点击这里: React 新特性讲解及实例 什么是 Hooks Hook 是 React 16.8 的新增特性.它可以让你在不编写 类组件 的情况下使用 ...

  4. iOS7新特性的兼容性处理方法 之三

    转载有人性!转载自 这篇文章 不管适配iOS7,或者是iOS7新项目适配设计师也好,在状态栏.导航栏以及导航按钮往往会是比较难解决的.这篇文章就很好地解决了大部分情况. 近期,跟大多数开发者一样,我也 ...

  5. iOS新特性框架、仿微信图片浏览、视频监控、爱心动画、文字适配等源码

    iOS精选源码 iOS一个看电影.电视剧集合 HDCinema 一个非常简易的新特性集成框架NewFeatures 全自动化的文字适配 仿微信朋友圈图片浏览器 iOS你的爱心❤️动画源码 一个类似系统 ...

  6. linux禁止普通用户切换,Linux系统如何禁止普通用户切换root?

    Linux系统如何禁止普通用户切换root? 在上正文之前,我们先将一些基础的Linux用户以及用户组的相关命令: 1.添加用户 useradd [-g group] [-d user_home_di ...

  7. dgesForExtendedLayout ios7新特性

    edgesForExtendedLayout是一个类型为UIExtendedEdge的属性,指定边缘要延伸的方向. 因为iOS7鼓励全屏布局,它的默认值很自然地是UIRectEdgeAll,四周边缘均 ...

  8. edgesForExtendedLayout ios7新特性

    为什么80%的码农都做不了架构师?>>>    edgesForExtendedLayout是一个类型为UIExtendedEdge的属性,指定边缘要延伸的方向. 因为iOS7鼓励全 ...

  9. iOS7新特性的兼容性处理方法

    搬砖也得尊重出处!转载自 这篇文章 1.UINavigationBar 在iOS7以下所有的UINavigationBar都采用拟物化的界面,给人的感觉很有立体感,但是在iOS7中所有的界面使用的扁平 ...

最新文章

  1. python画图简单代码-简单画图 - python代码库 - 云代码
  2. Nagios/Postfix 转发警报邮件到Office365
  3. 【杂谈】您想要的docker环境来了!
  4. 前端应该掌握的网络知识(1)
  5. Opencv——图像膨胀
  6. 桥接模式(Bridge)解析例子
  7. STM8单片机ADC模拟看门狗功能实现
  8. Android Material Components – MaterialAlertDialog
  9. MySQL数据表格导入导出
  10. [置顶] 从工作流引擎设计来看人精神活动的一些问题
  11. 计算机软件配置项csci
  12. 示坡线高程判断_地理示坡线
  13. 实验二 预测分析算法的设计与实现
  14. iphone个系列尺寸_不同尺寸的iphone截图看起来一样大吗?
  15. 销售书籍推荐:做销售你究竟该看什么书?
  16. (IS 19)wav2vec: Unsupervised Pre-training for Speech Recognition
  17. 工控安全PLC固件逆向一
  18. [FAQ11783][NW]MVNO忽略国内漫游(ignore national roaming)
  19. 软件测试基础篇(3)
  20. laravel excel导入导出box/spout

热门文章

  1. 前端国际化如何对中文——>英文自动化翻译小demo
  2. 软件测试项目经理自我评价表,测试工程师自我评价范例列表
  3. 小白打boss之路——2020fintech训练营数据赛道
  4. 个人项目——中小学数学卷子自动生成程序
  5. 安装RapidDesign_v1.3.0.Cracked.DX10.3.Rio
  6. 基于STC单片机的两轮平衡车设计
  7. java计算机毕业设计的洗衣店订单管理系统源码+数据库+系统+lw文档+mybatis+运行部署
  8. springboot+APP基于Android的洗衣店预约系统毕业设计源码260839
  9. MyBatis07:使用注解开发
  10. 物理科普读物推荐:《物理精神》——人类文明创新的原动力