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

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

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

/**

*  自定义的动画类

*  实现协议------>@protocol UIViewControllerAnimatedTransitioning

*  这个接口负责切换的具体内容,也即“切换中应该发生什么”

*/

@interface MTHCustomAnimator : NSObject

@end

@implementation MTHCustomAnimator

// 系统给出一个切换上下文,我们根据上下文环境返回这个切换所需要的花费时间

- (NSTimeInterval)transitionDuration:(id)transitionContext

{

return 1.0;

}

// 完成容器转场动画的主要方法,我们对于切换时的UIView的设置和动画都在这个方法中完成

- (void)animateTransition:(id)transitionContext

{

// 可以看做为destination ViewController

UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];

// 可以看做为source ViewController

UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];

// 添加toView到容器上

[[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 ()

@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;

// Push

UIButton *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];

// Present

UIButton *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) 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 )navigationController:(UINavigationController*)navigationController                          interactionControllerForAnimationController:(id )animationController

{

/**

*  在非交互式动画效果中,该方法返回 nil

*  交互式转场,自我理解意思是,用户能通过自己的动作来(常见:手势)控制,不同于系统缺省给定的push或者pop(非交互式)

*/

return _interactionController;

}

#pragma mark - Transitioning Delegate (Modal)

// 前2个用于动画

-(id)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source

{

self.minToMaxAnimator.animationType = AnimationTypePresent;

return _minToMaxAnimator;

}

-(id)animationControllerForDismissedController:(UIViewController *)dismissed

{

self.minToMaxAnimator.animationType = AnimationTypeDismiss;

return _minToMaxAnimator;

}

// 后2个用于交互

- (id )interactionControllerForPresentation:(id )animator

{

return _interactionController;

}

- (id )interactionControllerForDismissal:(id )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];

}

// 结束后一定要置为nil

self.interactionController = nil;

}

}

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

@implementation PDTransitionAnimator

#define Switch_Time 1.2

- (NSTimeInterval)transitionDuration:(id )transitionContext {

return Switch_Time;

}

#define Button_Width 50.f

#define Button_Space 10.f

- (void)animateTransition:(id )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,所以方法将在该设置应用在视图上了之后才进行快照,于是乎屏幕空空如也。另外就是……你可以对快照再进行快照……继续快照……

原文:http://blog.csdn.net/hmt20130412/article/details/39080445

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

推荐阅读更多精彩内容