容器转场

iOS7之前,ViewController切换主要有4种方式:
1.Push/Pop NavigationViewController
2.Present and dismis Modal
3.UITabBarController
4.addChildViewController

iOS5添加函数:
- (void)transitionFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(5_0);

iOS7添加新转场方式:
1.非交互式切换,即定义一种从一个VC到另一个VC的动画效果,切换的时候自动播放
2.交互式切换,这种方式同样需要定义动画效果,只是这个动画效果会根据跟随交互式手势来切换VC并同时播放动画效果
关键API:
1.动画控制器(AnimationControllers)
遵循UIViewControllerAnimatedTransitioning协议,负责实际执行动画,切换的具体内容,也即“切换中应该发生什么”
2.交互控制器(InteractionControllers)
遵循UIViewControllerInteractiveTransitioning协议,负责控制可交互式的转场,用一个百分比来控制交互式切换的过程
常用方法:
-(void)updateInteractiveTransition:(CGFloat)percentComplete 切换更新视图百分比,一般通过手势识别的长度之类的来计算一个值,然后进行更新。之后的例子里会看到详细的用法
-(void)cancelInteractiveTransition 报告交互取消,返回切换前的状态
-(void)finishInteractiveTransition 报告交互完成,更新到切换后的状态
3.转场代理控制器(TransitioningDelegatesControllers)
遵循UIViewControllerTransitioningDelegate协议,负责提供动画控制器和交互控制器
4.转场上下文(TransitioningContexts)
遵循UIViewControllerContextTransitioning 协议,负责定义转场需要的元数据(转场过程中所参与的视图控制器与视图)
5.转场协调器(TransitionCoordinators)
遵循UIViewControllerTransitionCoordinator 协议,负责处理运行转场动画时,并行运行其他动画
····苹果给我们开发者提供的是都是协议接口,所以我们能够很好的单独提出来写成一个个类,在里面实现我们各种自定义效果

1.非交互式转场实现范例:
1.自定义动画控制器,遵循UIViewControllerAnimatedTransitioning协议
@interface MyAnimator : NSObject<UIViewControllerAnimatedTransitioning>
@end

#import "MyAnimator.h"
@implementation MyAnimator
#pragma mark------------------------ UIViewControllerAnimatedTransitioning
//该方法需要自己根据视图切换上下文transitionContext参数,返回切换所需时间
-(NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext{
return 1.0;
}

//完成容器转场动画的主要方法,我们对于切换时的UIView的设置和动画都在这个方法中完成,转场的元数据从切换上下文transitionContext参数对象获取
-(void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
//从上下文对象拿到前后两视图
UIViewController *toViewController=[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromViewController=[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

//拿到后视图的界面view进行属性设置
UIView *toView=toViewController.view;
toView.alpha=0.0;

//添加后视图的界面view到上下文容器中
[[transitionContext containerView]addSubview:toView];

//创建动画
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
//动画效果有很多,这里就展示个左偏移
fromViewController.view.transform = CGAffineTransformMakeTranslation(-[UIScreen mainScreen].bounds.size.width, 0);
toViewController.view.alpha = 1.0;
} completion:^(BOOL finished) {
fromViewController.view.transform = CGAffineTransformIdentity;
//声明过渡结束-->一定要在过渡结束时调用completeTransition:方法
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
2.确定转场代理控制器
代理导航栏转场的需要遵循UINavigationControllerDelegate;
代理模态视图转场的需要遵循UIViewControllerTransitioningDelegate
@interface FirstViewController : UIViewController<UINavigationControllerDelegate,UIViewControllerTransitioningDelegate>
@end

#import "FirstViewController.h"
#import "SecondViewController.h"
#import "MyAnimator.h"
//自身为转场代理控制器
@implementation FirstViewController{
SecondViewController *secondVC;
MyAnimator *myAnimator;//动画控制器
}
-(void)viewDidLoad{
[super viewDidLoad];
myAnimator=[[MyAnimator alloc]init];
secondVC=[[SecondViewController alloc]init];
//设置后视图模态转场代理控制器为自己
secondVC.transitioningDelegate=self;
//设置导航控制器代理为自己
self.navigationController.delegate=self;
}
-(IBAction)pushAction:(id)sender {
//非交互的转场,指的是完全按照系统指定的切换机制,用户无法中途取消或者控制进度切换.
[self.navigationController pushViewController:secondVC animated:YES];
}
-(IBAction)presentAction:(id)sender {
[self presentViewController:secondVC animated:YES completion:nil];
}
3.实现转场代理控制器的协议方法
如果既是导航栏转场代理又是模态视图转场代理,那么需要两套代理方法都实现
两套协议方法中每一套都分为两类型,分别是交互式需要实现的协议方法与非交互式需要实现的协议方法
#pragma mark - UINavigationControllerDelegate
//非交互
//指定转场动画控制器,返回nil会使用系统默认动画控制器
-(id<UIViewControllerAnimatedTransitioning>) navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
if (operation == UINavigationControllerOperationPush) {//指定那种切换类型
return myAnimator;
}else{
return nil;
}
}

#pragma mark - TransitioningDelegate (Modal)
// 非交互
//指定模态跳转使用的动画控制器
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
return myAnimator;
}
//指定模态返回使用的动画控制器,返回nil会默认使用系统的动画控制器
-(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
return nil;
}

2.交互式转场实现范例:
1.自定义动画控制器,遵循UIViewControllerAnimatedTransitioning协议
2.确定转场代理控制器
//为该转场代理控制器添加交互控制器成员变量(手势交互以及协议方法中需要用到)
UIPercentDrivenInteractiveTransition *interactionController;//交互控制器
//为该转场代理控制器添加手势交互
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(didClickPanGestureRecognizer:)];
[self.navigationController.view addGestureRecognizer:panRecognizer];
//实现手势交互方法
- (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){
interactionController= [[UIPercentDrivenInteractiveTransition alloc] init];
//[self.navigationController pushViewController:secondVC animated:YES];
[self presentViewController:secondVC animated:YES completion:nil];
}
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
// 获取手势在视图上偏移的坐标
CGPoint translation = [recognizer translationInView:view];
// 根据手指拖动的距离计算一个百分比,切换的动画效果也随着这个百分比来走
CGFloat distance = fabs(translation.x / CGRectGetWidth(view.bounds));
// 交互控制器控制动画的进度
[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) {
[interactionController finishInteractiveTransition];
} else {
[interactionController cancelInteractiveTransition];
}
// 结束后一定要置为nil
interactionController = nil;
}
}
3.实现转场代理控制器的协议方法
#pragma mark - UINavigationControllerDelegate
//交互
-(id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController*)navigationController interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>)animationController{
return interactionController;
}
#pragma mark - TransitioningDelegate (Modal)
//交互
-(id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator{
return interactionController;
}
-(id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator{
return nil;
}

//继承UINavigationController编写自定义NavigationController,实现手势pop上一个ViewController缩放效果
#import <UIKit/UIKit.h>

@interface MyNavigationController : UINavigationController <UIGestureRecognizerDelegate>

@end

#import "MyNavigationController.h"
#import <QuartzCore/QuartzCore.h>

@interface MyNavigationController (){
CGPoint startTouch;
UIImageView *lastScreenShotView;
UIView *blackMask;
}
@property (nonatomic,retain) UIView *backgroundView;
@property (nonatomic,retain) NSMutableArray *screenShotsList;
@end
@implementation MyNavigationController
- (void)viewDidLoad{
[super viewDidLoad];
self.interactivePopGestureRecognizer.enabled = NO;//用这种效果就得把系统默认手势pop方式禁用掉
self.screenShotsList = [NSMutableArray array];
UIImage *capturedImage = [self capture];
if (capturedImage) {
[self.screenShotsList addObject:capturedImage];
}
UIPanGestureRecognizer *recognizer = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(paningGestureReceive:)];
recognizer.delegate = self;
[self.view addGestureRecognizer:recognizer];
}

// 重写push
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
UIImage *capturedImage = [self capture];//还没push到新controller时先获取当前屏幕快照保留起来
if (capturedImage) {
[self.screenShotsList addObject:capturedImage];
}
[super pushViewController:viewController animated:animated];
}

// 重写pop
- (UIViewController *)popViewControllerAnimated:(BOOL)animated{
[self.screenShotsList removeLastObject];//pop方式返回直接把快照数组最后一张快照删除
return [super popViewControllerAnimated:animated];
}

// 获取屏幕最顶ViewController的View的快照
- (UIImage *)capture{
UIView *topView=[[UIApplication sharedApplication]keyWindow].rootViewController.view;
UIGraphicsBeginImageContextWithOptions(topView.bounds.size, topView.opaque, 0.0);
[topView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return img;
}

// 移动的时候设置背景图中的快照图位置和背景图的阴影图透明度
- (void)moveViewWithX:(float)x{
NSLog(@"Move to:%f",x);
x = x>320?320:x;
x = x<0?0:x;
UIView *topView=[[UIApplication sharedApplication]keyWindow].rootViewController.view;
CGRect frame = topView.frame;
frame.origin.x = x;//计算originX
topView.frame = frame;//改变快照原点坐标
float scale = (x/6400)+0.95;//计算缩放比例
float alpha = 0.4 - (x/800);
lastScreenShotView.transform = CGAffineTransformMakeScale(scale, scale);//缩放
blackMask.alpha = alpha;

}

//pan手势回调
- (void)paningGestureReceive:(UIPanGestureRecognizer *)recoginzer{
// 当前是navigation的根视图时不响应
if (self.viewControllers.count <= 1) return;

UIView *keyWindow=[[UIApplication sharedApplication]keyWindow];
UIView *topView=[[UIApplication sharedApplication]keyWindow].rootViewController.view;
//获取手势在整个屏幕的坐标
CGPoint touchPoint = [recoginzer locationInView:keyWindow];
//手势开始的时候
if (recoginzer.state == UIGestureRecognizerStateBegan) {
startTouch = touchPoint;//记录手势开始的坐标
if (!self.backgroundView)
{
CGRect frame = topView.frame;
self.backgroundView = [[[UIView alloc]initWithFrame:CGRectMake(0, 0, frame.size.width , frame.size.height)]autorelease];
self.backgroundView.backgroundColor=[UIColor yellowColor];
//在背景层上添加阴影层
blackMask = [[[UIView alloc]initWithFrame:CGRectMake(0, 0, frame.size.width , frame.size.height)]autorelease];
blackMask.backgroundColor = [UIColor redColor];
[self.backgroundView addSubview:blackMask];
//在topView的父视图中插入backgroundView在topView的下层
[topView.superview insertSubview:self.backgroundView belowSubview:topView];
}
//隐藏插入的背景层
self.backgroundView.hidden = NO;

//把存在的上一次添加的快照图移除
if (lastScreenShotView) {
[lastScreenShotView removeFromSuperview];
}

//拿出navigation的上一个viewcontroller快照图插入到背景图中阴影图的下面
UIImage *lastScreenShot = [self.screenShotsList lastObject];
lastScreenShotView = [[[UIImageView alloc]initWithImage:lastScreenShot]autorelease];
[self.backgroundView insertSubview:lastScreenShotView belowSubview:blackMask];
}else if (recoginzer.state == UIGestureRecognizerStateEnded){//手势结束的时候判断要不要pop
if (touchPoint.x - startTouch.x > 50){//pop
[UIView animateWithDuration:0.3 animations:^{
[self moveViewWithX:320];
} completion:^(BOOL finished) {
[self popViewControllerAnimated:NO];
CGRect frame = topView.frame;
frame.origin.x = 0;
topView.frame = frame;
self.backgroundView.hidden = YES;
}];
}
else{//不pop
[UIView animateWithDuration:0.3 animations:^{
[self moveViewWithX:0];
} completion:^(BOOL finished) {
self.backgroundView.hidden = YES;
}];
}
return;
}else if (recoginzer.state == UIGestureRecognizerStateCancelled){//手势取消的时候不pop
[UIView animateWithDuration:0.3 animations:^{
[self moveViewWithX:0];
} completion:^(BOOL finished) {
self.backgroundView.hidden = YES;
}];
return;
}else if (recoginzer.state == UIGestureRecognizerStateChanged){//剩下手势移动的时候
[self moveViewWithX:touchPoint.x - startTouch.x];
}
}

@end

时间: 2024-08-02 21:06:21

容器转场的相关文章

自定义 ViewController 容器转场

本文转载至 http://blog.csdn.net/yongyinmg/article/details/40621463 在话题 #5 中,Chris Eidhof 向我们介绍了 iOS7 引入的新特性自定义 View Controller 转场. 他给出了一个 结论: 我们在本文只探讨了在 navigation controller 中的两个 view controller 之间的转场动画,但是这些做法在 tab bar controller 或者任何你自己定义的 view controll

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

@上一章,介绍了主要的iOS7所增加的API,可以发现,它们不是一个个死的方法,苹果给我们开发者提供的是都是协议接口,所以我们能够很好的单独提出来写成一个个类,在里面实现我们各种自定义效果.        1.先来看看实现UIViewControllerAnimatedTransitioning的自定义动画类 /** * 自定义的动画类 * 实现协议------>@protocol UIViewControllerAnimatedTransitioning * 这个接口负责切换的具体内容,也即&qu

iOS7新特性 ViewController转场切换(一) 以前总结和关键API介绍

@在iOS7之前,View Controller的切换主要有4种: 1. Push/Pop,NavigationViewController 2. Present and dismis Modal 3. UITabBarController 4. addChildViewController(一般用于自定义的继承于 UIViewController 的容器子类) iOS5,调用- (void)transitionFromViewController:(UIViewController *)fro

iOS UIViewController API解读

/*UIViewController is a generic controller base class that manages a view. It has methods that are calledwhen a view appears or disappears. Subclasses can override -loadView to create their custom view hierarchy, or specify a nib name to be loadedaut

iOS中关于动画效果的要点

在系统并深入学习iOS动画的过程中,不得不说是个痛苦的过程.没有任何书系统的讲解这方面的知识,网上的文章都讲的支离破碎:很幸运的看到了http://objccn.io这个网站:即使如此,还是花了三天时间:这个是对整体概念模糊到不断清晰,再逐步理顺,最后总结归纳为几个关键点.我想这辈子应该都忘记不了了. iOS上的动画效果绝对赞:最常见的uitable动态效果,当手指在屏幕上下滑动时,列表会跟随其一起上下活动:如果猛的往上一推,还可以看到列表的惯性作用下,还会不断滚动,同时慢慢减速.这个过程根本不

UIViewController全部API的学习。

/* UIViewController is a generic controller base class that manages a view.  It has methods that are called when a view appears or disappears. Subclasses can override -loadView to create their custom view hierarchy, or specify a nib name to be loaded

UIViewController所有API的学习。

<欢迎大家加入iOS开发学习交流群:QQ529560119> /* ?? ? UIViewController is a generic controller base class that manages a view.? It has methods that are called ?? ? when a view appears or disappears. ? ? ? ?? ? Subclasses can override -loadView to create their cust

iOS7新特性 ViewController转场切换(三) 自定义视图控制器容器的切换---非交互式

@继续前面的内容,这一章,主要介绍自定义ViewController容器上视图VC的切换.先来看看系统给我们提供的容器控制器 UINavigationController和UITabBarController 都有一个NSArray类型的属性viewControllers,很明显,存储的就是需要切换的视图VC.同理,我们定义一个ContainerViewController,是UIViewController的直接子类,用来作为容器依托,额,其他属性定义详见代码吧,这里不多说了.(PS:原先我进

C++容器学习,与结构体排序和set来一场邂逅

最近学习C++容器,积累一下.下面介绍set和multiset,并使用sort对结构体进行排序.C++之路漫漫其修远兮! 一.对结构体进行排序 // sort_struct.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #include <vector> #include <algorithm> using namespace std; typedef struct e