【IOS开发基础系列】UIViewController专题

1 UIViewController机制

1.1 生命周期

UIViewController生命周期

    理解view的生命周期:

        在UIViewController中,view(黑体的view指的是controller的view属性)有两个循环:加载和卸载循环。当程序的一部分向controller请求view的指针且view不在内存中时,view会进入加载循环,controller会将view加载入内存。

        当程序接收到内存警告时,controller会尝试卸载view,在卸载循环中,controller尝试释放它的view对象并返回到原始的无view状态(当它不在屏幕上显示时,这个条件的判断到底是根据view的结构来还是根据用户视觉来,我尚不清楚),直到view下次被请求。

        在加载卸载循环中,controller处理的大部分逻辑。但是如果我们的controller还“持有”着view的后代view时,或者还有其他后续操作需要进行时,我们可以重载特定函数(后面会介绍到)来另行处理。

    加载循环:

    1 程序请求了controller的view.

    2 如果view当前不在内存中,controller调用loadview函数。

    3 loadView 进行如下操作:

        a) 如果你重载了这个函数,你应该自己创建必要的views并且将一个非nil值赋给view属性

        b) 如果你没有重载这个函数,默认实现会使用controller的nibNamenibBundle属性来尝试从nib文件加载view。如果没有找到nib文件,它尝试寻找一个与view controller类名匹配(viewControllerClassName.nib)的nib文件。

        c) 如果没有可用的nib文件,那么它创建一个空的UIView作为它的view

    4 controller调用viewDidLoad方法来执行一些加载时(加载时一词,相对于编译时、运行时)任务。


        程序可以重载loadView 和 viewDidLoad来执行一些任务:

    卸载循环:

    1 程序收到内存警告;

    2 每个view controller调用 didReceiveMemoryWarning:

        a) If you override this method, you should use it to release any custom data that

your view controller object no longer needs. You should not use it to release

your view controller’s view. You must call super at some point in your implementation to

perform the default behavior.(iOS3.0以后不建议重载这个函数来进行额外的清除操作,使用viewDidUnload).

        b) 默认实现会在确定可以安全地释放view时释放掉view

        如果controller释放了它的view, 它会调用 viewDidUnload.可以重载这个函数来进行额外的清理操作(不要清除view和那些加载循环中无法rebuild的数据)。BTW:didReceiveMemoryWarning到底应该怎么用 当程序接到内存警告时View Controller将会收到这个消息:didReceiveMemoryWarning从iOS3.0开始,不需要重载这个函数,把释放内存的代码放到viewDidUnload中去。这个函数的默认实现是:检查controller是否可以安全地释放它的view(这里加粗的view指的是controller的view属性),比如view本身没有superview并且可以被很容易地重建(从nib或者loadView函数)。如果view可以被释放,那么这个函数释放view并调用viewDidUnload。你可以重载这个函数来释放controller中使用的其他内存。但要记得调用这个函数的super实现来允许父类(一般是UIVIewController)释放view。如果你的ViewController保存着view的子view的引用,那么,在早期的iOS版本中,你应该在这个函数中来释放这些引用。而在iOS3.0或更高版本中,你应该在viewDidUnload中释放这些引用。

        感谢:http://www.cnblogs.com/Piosa/archive/2012/02/22/2363258.html


2 开发技巧

2.1 常用开发技巧

2.1.1 手动添加并呈现带导航条的子VC

    HJAddCityVC * addVC = [[AQAddCityVC alloc] init];

    UINavigationController * addNavController = [[UINavigationController alloc] initWithRootViewController: addVC];

    [addNavController addChildViewController: addVC];

    [rootVC.view addSubview: addNavController.view];

    [rootVC addChildViewController: addNavController];

    [addVC.view setFrame:rootVC.view.frame];

   [addVC.view setBackgroundColor:[UIColor whiteColor]];


2.1.2 手动移除带导航条的子VC

        视图View与控制器都需要移除,缺一不可。

-(void) closeView: (id)sender

{

    [self.navigationController.view removeFromSuperview];

    [self.navigationController removeFromParentViewController];

}

2.2 常见问题

2.2.1 Unbalancedcalls to begin/end appearance transitions for

Unbalanced calls to begin/endappearance transitions for

        原因就是上次动画还没结束,然后又开始了新的动画。  这样就导致不能成功切换页面,而是一个白色无内容的页面。

        出现unbalanced calls to begin/end appearance transitions for uiviewcontroller这样的log,其原因就是在容器类的UIViewController(如,UINavigationController, UITabBarController)中动画没做完,然后又开始新的动画.。解决办法就是让动画完后再做新的动画。

    解决方法1:去掉动画

    解决方法2:监听当前view的动画是否完成


    问题场景:

        此类问题比较容易出现在两个vc连续push或者pop的时候,简单做法就是将push或者pop动画标记置为no。

    彻底解决方法是:

        在第一个被push的VC的viewDidAppear方法中再去push第二个VC。


3 ViewController切换

UIViewControllerTransitioning.h

@protocol UIViewControllerContextTransitioning

在iOS7之前,View Controller的切换主要有4种:

1. Push/Pop,NavigationViewController

2. Present and dismiss Modal

3. UITabBarController

4. addChildViewController(一般用于自定义的继承于 UIViewController 的容器子类)


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

    (1)前面3种方法这里就不多说了,很常见的系统方法.至于第四种,我在前面文章-剖析网易标签栏的效果中已经做了阐述,但是它提供的容器转场动画只可以实现一些简单的UIView动画,但是难以重用,耦合高.

    (2)关键的API:

    A.动画控制器 (Animation Controllers) 遵从 UIViewControllerAnimatedTransitioning 协议,并且负责实际执行动画。

    B.交互控制器 (Interaction Controllers) 通过遵从 UIViewControllerInteractiveTransitioning 协议来控制可交互式的转场。

    C.转场代理 (Transitioning Delegates) 根据不同的转场类型方便的提供需要的动画控制器和交互控制器。

    其中UINavigationControllerDelegate delegate 中新增了2个方法给NavigationController

UIViewControllerTransitioningDelegate 新增transitioningDelegate 给Modal的Present和Dismis

UITabBarControllerDelegate delegate

- (id )tabBarController:(UITabBarController*)tabBarController

interactionControllerForAnimationController:(id )animationController NS_AVAILABLE_IOS(7_0);- (id)tabBarController:(UITabBarController *)tabBarController

animationControllerForTransitionFromViewController:(UIViewController*)fromVC

toViewController:(UIViewController *)toVCNS_AVAILABLE_IOS(7_0);

    D.转场上下文 (Transitioning Contexts) 定义了转场时需要的元数据,比如在转场过程中所参与的视图控制器和视图的相关属性。 转场上下文对象遵从 UIViewControllerContextTransitioning 协议,并且这是由系统负责生成和提供的。

    E.转场协调器(Transition Coordinators) 可以在运行转场动画时,并行的运行其他动画。 转场协调器遵从 UIViewControllerTransitionCoordinator 协议。


3.1 UIViewControllerTransitioning  API说明

3.1.1 UIViewControllerContextTransitioning

        这个接口用来提供切换上下文给开发者使用,包含了从哪个VC到哪个VC等各类信息,一般不需要开发者自己实现。具体来说,iOS7的自定义切换目的之一就是切换相关代码解耦,在进行VC切换时,做切换效果实现的时候必须要需要切换前后VC的一些信息,系统在新加入的API的比较的地方都会提供一个实现了该接口的对象,以供我们使用。

        对于切换的动画实现来说(这里先介绍简单的动画,在后面我会再引入手势驱动的动画),这个接口中最重要的方法有:

-(UIView *)containerView;

        VC切换所发生的view容器,开发者应该将切出的view移除,将切入的view加入到该view容器中。


-(UIViewController)viewControllerForKey:(NSString )key;

        提供一个key,返回对应的VC。现在的SDK中key的选择只有UITransitionContextFromViewControllerKey和UITransitionContextToViewControllerKey两种,分别表示将要切出和切入的VC。


-(CGRect)initialFrameForViewController:(UIViewController*)vc;

        某个VC的初始位置,可以用来做动画的计算。


-(CGRect)finalFrameForViewController:(UIViewController*)vc;

        与上面的方法对应,得到切换结束时某个VC应在的frame。


-(void)completeTransition:(BOOL)didComplete;

        向这个context报告切换已经完成。


3.1.2 UIViewControllerAnimatedTransitioning

        这个接口负责切换的具体内容,也即“切换中应该发生什么”。开发者在做自定义切换效果时大部分代码会是用来实现这个接口。它只有两个方法需要我们实现:

-(NSTimeInterval)transitionDuration:(id)transitionContext;

        系统给出一个切换上下文,我们根据上下文环境返回这个切换所需要的花费时间(一般就返回动画的时间就好了,SDK会用这个时间来在百分比驱动的切换中进行帧的计算,后面再详细展开)。


-(void)animateTransition:(id)transitionContext;

        在进行切换的时候将调用该方法,我们对于切换时的UIView的设置和动画都在这个方法中完成。


// This is a convenience and if implemented will be invoked by the system when the transition context's completeTransition: method is invoked.

- (void) animationEnded: (BOOL)transitionCompleted;


3.1.3 UIViewControllerTransitioningDelegate

        这个接口的作用比较简单单一,在需要VC切换的时候系统会像实现了这个接口的对象询问是否需要使用自定义的切换效果。这个接口共有四个类似的方法:

-(id)animationControllerForPresentedController:(UIViewController)presented presentingController:(UIViewController )presentingsourceController:(UIViewController *)source;


-(id)animationControllerForDismissedController:(UIViewController *)dismissed;


-(id)interactionControllerForPresentation:(id )animator;


-(id)interactionControllerForDismissal:(id )animator;

        前两个方法是针对动画切换的,我们需要分别在呈现VC和解散VC时,给出一个实现了UIViewControllerAnimatedTransitioning接口的对象(其中包含切换时长和如何切换)。后两个方法涉及交互式切换,之后再说。    


3.1.4 Demo

        还是那句话,一百行的讲解不如一个简单的小Demo,于是..it’s demo time~ 整个demo的代码我放到了github的这个页面上,有需要的朋友可以参照着看这篇文章。

        我们打算做一个简单的自定义的modalViewController的切换效果。普通的present modal VC的效果大家都已经很熟悉了,这次我们先实现一个自定义的类似的modal present的效果,与普通效果不同的是,我们希望modalVC出现的时候不要那么乏味的就简单从底部出现,而是带有一个弹性效果(这里虽然是弹性,但是仅指使用UIView的模拟动画,而不设计iOS 7的另一个重要特性UIKit Dynamics。用UIKit Dynamics当然也许可以实现更逼真华丽的效果,但是已经超出本文的主题范畴了,因此不在这里展开了。关于UIKit Dynamics,可以参看我之前关于这个主题的一篇介绍)。我们首先实现简单的ModalVC弹出吧..这段非常基础,就交待了一下背景,非初级人士请跳过代码段..

        先定义一个ModalVC,以及相应的protocal和delegate方法:

//ModalViewController.h

@class ModalViewController;

@protocol ModalViewControllerDelegate 

    -(void) modalViewControllerDidClickedDismissButton: (ModalViewController *)viewController;

@end

@interface ModalViewController : UIViewController

    @property (nonatomic, weak) id delegate;

@end

//ModalViewController.m

- (void)viewDidLoad

{

    [super viewDidLoad];

    // Do any additional setup after loading the view.

    self.view.backgroundColor = [UIColor lightGrayColor];

    UIButton *button = [UIButton buttonWithType: UIButtonTypeRoundedRect];

    button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);

    [button setTitle: @"Dismiss me" forState: UIControlStateNormal];

    [button addTarget: self action: @selector(buttonClicked:) forControlEvents: UIControlEventTouchUpInside];

    [self.view addSubview: button];

}

-(void) buttonClicked: (id)sender

{

    if(self.delegate && [self.delegate respondsToSelector:@selector(modalViewControllerDidClickedDismissButton:)]) {

        [self.delegate modalViewControllerDidClickedDismissButton: self];

    }

}

        这个是很标准的modalViewController的实现方式了。需要多嘴一句的是,在实际使用中有的同学喜欢在-buttonClicked:中直接给self发送dismissViewController的相关方法。在现在的SDK中,如果当前的VC是被显示的话,这个消息会被直接转发到显示它的VC去。但是这并不是一个好的实现,违反了程序设计的哲学,也很容易掉到坑里,具体案例可以参看这篇文章的评论。

        所以我们用标准的方式来呈现和解散这个VC:

//MainViewController.m

- (void)viewDidLoad

{

    [super viewDidLoad];

    // Do any additional setup after loading the view.

    UIButton *button = [UIButton buttonWithType: UIButtonTypeRoundedRect];

    button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);

    [button setTitle: @"Click me" forState: UIControlStateNormal];

    [button addTarget: self action: @selector(buttonClicked:) forControlEvents: UIControlEventTouchUpInside];

    [self.view addSubview: button];

}

-(void) buttonClicked: (id)sender

{

    ModalViewController *mvc =  [[ModalViewController alloc] init];

    mvc.delegate = self;

    [self presentViewController: mvc animated: YES completion: nil];

}

- (void) modalViewControllerDidClickedDismissButton:(ModalViewController *)viewController

{

    [self dismissViewControllerAnimated: YES completion: nil];

}

        测试一下,没问题,然后我们可以开始实现自定义的切换效果了。首先我们需要一个实现了UIViewControllerAnimatedTransitioning的对象..嗯,新建一个类来实现吧,比如BouncePresentAnimation:

//BouncePresentAnimation.h

@interface BouncePresentAnimation : NSObject

@end

//BouncePresentAnimation.m

- (NSTimeInterval)transitionDuration: (id )transitionContext

{

    return 0.8f;

}

- (void) animateTransition: (id )transitionContext

{

    // 1. Get controllers from transition context

    UIViewController *toVC = [transitionContext viewControllerForKey: UITransitionContextToViewControllerKey];

    // 2. Set init frame for toVC

    CGRect screenBounds = [[UIScreen mainScreen] bounds];

    CGRect finalFrame = [transitionContext finalFrameForViewController: toVC];

    toVC.view.frame = CGRectOffset(finalFrame, 0, screenBounds.size.height);

    // 3. Add toVC's view to containerView

    UIView *containerView = [transitionContext containerView];

    [containerView addSubview: toVC.view];

    // 4. Do animate now

    NSTimeInterval duration = [self transitionDuration: transitionContext];

    [UIView animateWithDuration: duration delay: 0.0 usingSpringWithDamping: 0.6 initialSpringVelocity: 0.0 options: UIViewAnimationOptionCurveLinear animations:^{

        toVC.view.frame = finalFrame;

    } completion: ^(BOOL finished) {

        // 5. Tell context that we completed.

        [transitionContext completeTransition: YES];

    }];

}

        解释一下这个实现:

    1.我们首先需要得到参与切换的两个ViewController的信息,使用context的方法拿到它们的参照;

    2.对于要呈现的VC,我们希望它从屏幕下方出现,因此将初始位置设置到屏幕下边缘;

    3.将view添加到containerView中;

    4.开始动画。这里的动画时间长度和切换时间长度一致,都为0.8s。usingSpringWithDamping的UIView动画API是iOS7新加入的,描述了一个模拟弹簧动作的动画曲线,我们在这里只做使用,更多信息可以参看相关文档;(顺便多说一句,iOS7中对UIView动画添加了一个很方便的Category,UIViewKeyframeAnimations。使用其中方法可以为UIView动画添加关键帧动画)

    5.在动画结束后我们必须向context报告VC切换完成,是否成功(在这里的动画切换中,没有失败的可能性,因此直接pass一个YES过去)。系统在接收到这个消息后,将对VC状态进行维护。

        接下来我们实现一个UIViewControllerTransitioningDelegate,应该就能让它工作了。简单来说,一个比较好的地方是直接在MainViewController中实现这个接口。在MainVC中声明实现这个接口,然后加入或变更为如下代码:

@interface MainViewController ()

    @property (nonatomic, strong) BouncePresentAnimation *presentAnimation;

@end

@implementation MainViewController

- (id) initWithNibName: (NSString *)nibNameOrNil bundle: (NSBundle *)nibBundleOrNil

{

    self = [super initWithNibName: nibNameOrNil bundle: nibBundleOrNil];

    if(self) {

        // Custom initialization

        _presentAnimation = [BouncePresentAnimation new];

    }

    return self;

}

-(void) buttonClicked: (id)sender

{

    //...

    mvc.transitioningDelegate = self;

    //...

}

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

{

    return self.presentAnimation;

}

        Believe or not, we have done. 跑一下,应该可以得到如下效果:

        BouncePresentAnimation的实际效果


3.2 手势驱动的百分比切换

        iOS7引入了一种手势驱动的VC切换的方式(交互式切换)。如果你使用系统的各种应用,在navViewController里push了一个新的VC的话,返回时并不需要点击左上的Back按钮,而是通过从屏幕左侧划向右侧即可完成返回操作。而在这个操作过程中,我们甚至可以撤销我们的手势,以取消这次VC转移。在新版的Safari中,我们甚至可以用相同的手势来完成网页的后退功能(所以很大程度上来说屏幕底部的工具栏成为了摆设)。

        首先是UIViewControllerContextTransitioning,刚才提到这个是系统提供的VC切换上下文,如果您深入看了它的头文件描述的话,应该会发现其中有三个关于InteractiveTransition的方法,正是用来处理交互式切换的。但是在初级的实际使用中我们其实可以不太理会它们,而是使用iOS 7 SDK已经给我们准备好的一个现成转为交互式切换而新加的类:UIPercentDrivenInteractiveTransition。

3.2.1 UIPercentDrivenInteractiveTransition

        这是一个实现了UIViewControllerInteractiveTransitioning接口的类,为我们预先实现和提供了一系列便利的方法,可以用一个百分比来控制交互式切换的过程。一般来说我们更多地会使用某些手势来完成交互式的转移(当然用的高级的话用其他的输入..比如声音,iBeacon距离或者甚至面部微笑来做输入驱动也无不可,毕竟想象无极限嘛..),这样使用这个类(一般是其子类)的话就会非常方便。我们在手势识别中只需要告诉这个类的实例当前的状态百分比如何,系统便根据这个百分比和我们之前设定的迁移方式为我们计算当前应该的UI渲染,十分方便。具体的几个重要方法:

- (void) updateInteractiveTransition: (CGFloat)percentComplete

        更新百分比,一般通过手势识别的长度之类的来计算一个值,然后进行更新。之后的例子里会看到详细的用法

-(void)cancelInteractiveTransition

        报告交互取消,返回切换前的状态

–(void)finishInteractiveTransition

        报告交互完成,更新到切换后的状态

3.2.2 UIViewControllerInteractiveTransitioning

@protocol UIViewControllerInteractiveTransitioning

        就如上面提到的,UIPercentDrivenInteractiveTransition只是实现了这个接口的一个类。为了实现交互式切换的功能,我们需要实现这个接口。因为大部分时候我们其实不需要自己来实现这个接口,因此在这篇入门中就不展开说明了,有兴趣的童鞋可以自行钻研。

        还有就是上面提到过的UIViewControllerTransitioningDelegate中的返回Interactive实现对象的方法,我们同样会在交互式切换中用到它们。


3.2.3 Demo

        Demo time again。在刚才demo的基础上,这次我们用一个向上划动的手势来吧之前呈现的ModalViewController给dismiss掉~当然是交互式的切换,可以半途取消的那种。

        首先新建一个类,继承自UIPercentDrivenInteractiveTransition,这样我们可以省不少事儿。

//SwipeUpInteractiveTransition.h

@interface SwipeUpInteractiveTransition : UIPercentDrivenInteractiveTransition

    @property (nonatomic, assign) BOOL interacting;

    - (void) wireToViewController: (UIViewController*)viewController;

@end


//SwipeUpInteractiveTransition.m

@interface SwipeUpInteractiveTransition()

    @property (nonatomic, assign) BOOL shouldComplete;

    @property (nonatomic, strong) UIViewController *presentingVC;

@end

@implementation SwipeUpInteractiveTransition

- (void) wireToViewController: (UIViewController *)viewController

{

    self.presentingVC = viewController;

    [self prepareGestureRecognizerInView: viewController.view];

}

- (void) prepareGestureRecognizerInView: (UIView*)view {

    UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget: self action: @selector(handleGesture:)];

    [view addGestureRecognizer: gesture];

}

- (CGFloat) completionSpeed

{

    return 1 - self.percentComplete;

}

- (void) handleGesture: (UIPanGestureRecognizer *)gestureRecognizer {

    CGPoint translation = [gestureRecognizer translationInView: gestureRecognizer.view.superview];

    switch(gestureRecognizer.state) {

        case UIGestureRecognizerStateBegan:

            // 1. Mark the interacting flag. Used when supplying it in delegate.

            self.interacting = YES;

            [self.presentingVC dismissViewControllerAnimated: YES completion: nil];

            break;

        case UIGestureRecognizerStateChanged: {

            // 2. Calculate the percentage of guesture

            CGFloat fraction = translation.y / 400.0;

            //Limit it between 0 and 1

            fraction = fminf(fmaxf(fraction, 0.0), 1.0);

            self.shouldComplete = (fraction > 0.5);

            [self updateInteractiveTransition: fraction];

            break;

        }

        case UIGestureRecognizerStateEnded:

        case UIGestureRecognizerStateCancelled: {

            // 3. Gesture over. Check if the transition should happen or not

            self.interacting = NO;

            if(!self.shouldComplete || gestureRecognizer.state == UIGestureRecognizerStateCancelled) {

                [self cancelInteractiveTransition];

            } 

            else{

                [self finishInteractiveTransition];

            }

            break;

        }

        default:

            break;

    }

}

@end

        有点长,但是做的事情还是比较简单的。

    1.我们设定了一个BOOL变量来表示是否处于切换过程中。这个布尔值将在监测到手势开始时被设置,我们之后会在调用返回这个InteractiveTransition的时候用到。

    2.计算百分比,我们设定了向下划动400像素或以上为100%,每次手势状态变化时根据当前手势位置计算新的百分比,结果被限制在0~1之间。然后更新InteractiveTransition的百分数。

    3.手势结束时,把正在切换的标设置回NO,然后进行判断。在2中我们设定了手势距离超过设定一半就认为应该结束手势,否则就应该返回原来状态。在这里使用其进行判断,已决定这次transition是否应该结束。

        接下来我们需要添加一个向下移动的UIView动画,用来表现dismiss。这个十分简单,和BouncePresentAnimation很相似,写一个NormalDismissAnimation的实现了UIViewControllerAnimatedTransitioning接口的类就可以了,本文里略过不写了,感兴趣的童鞋可以自行查看源码。

        最后调整MainViewController的内容,主要修改点有三个地方:

//MainViewController.m

@interface MainViewController ()

//...

// 1. Add dismiss animation and transition controller

    @property (nonatomic, strong) NormalDismissAnimation *dismissAnimation;

    @property (nonatomic, strong) SwipeUpInteractiveTransition *transitionController;

@end

@implementation MainViewController

- (id) initWithNibName: (NSString *)nibNameOrNil bundle: (NSBundle *)nibBundleOrNil

{

    //...

        _dismissAnimation = [NormalDismissAnimation new];

        _transitionController = [SwipeUpInteractiveTransition new];

    //...

}

- (void) buttonClicked: (id)sender

{

    //...

    // 2. Bind current VC to transition controller.

        [self.transitionController wireToViewController: mvc];

    //...

}

// 3. Implement the methods to supply proper objects.

- (id) animationControllerForDismissedController: (UIViewController *)dismissed

{

    return self.dismissAnimation;

}

- (id) interactionControllerForDismissal: (id)animator {

    return self.transitionController.interacting ? self.transitionController : nil;

}

    1.在其中添加dismiss时候的动画和交互切换Controller;

    2.在初始化modalVC的时候为交互切换的Controller绑定VC;

    3.为UIViewControllerTransitioningDelegate实现dismiss时候的委托方法,包括返回对应的动画以及交互切换Controller;

    完成了,如果向下划动时,效果如下:

    交互驱动的VC转移


3.3 关于iOS 7中自定义VC切换的一些总结

        demo中只展示了对于modalVC的present和dismiss的自定义切换效果,当然对与Navigation Controller的Push和Pop切换也是有相应的一套方法的。实现起来和dismiss十分类似,只不过对应UIViewControllerTransitioningDelegate的询问动画和交互的方法换到了UINavigationControllerDelegate中(为了区别push或者pop,看一下这个接口应该能马上知道)。另外一个很好的福利是,对于标准的navController的Pop操作,苹果已经替我们实现了手势驱动返回,我们不用再费心每个去实现一遍了,cheers~

        另外,可能你会觉得使用VC容器其提供的transition动画方法来进行VC切换就已经够好够方便了,为什么iOS7中还要引入一套自定义的方式呢。其实从根本来说它们所承担的是两类完全不同的任务:自定义VC容器可以提供自己定义的VC结构,并保证系统的各类方法和通知能够准确传递到合适的VC,它提供的transition方法虽然可以实现一些简单的UIView动画,但是难以重用,可以说是和containerVC完全耦合在一起的;而自定义切换并不改变VC的组织结构,只是负责提供view的效果,因为VC切换将动画部分、动画驱动部分都使用接口的方式给出,因此重用性非常优秀。在绝大多数情况下,精心编写的一套UIView动画是可以轻易地用在不同的VC中,甚至是不同的项目中的。

        需要特别一提的是,Github上的ColinEberhardt的VCTransitionsLibrary已经为我们提供了一系列的VC自定义切换动画效果,正是得益于iOS7中这一块的良好设计(虽然这几个接口的命名比较相似,在弄明白之前会有些confusing),因此这些效果使用起来非常方便,相信一般项目中是足够使用的了。而其他更复杂或者炫目的效果,亦可在其基础上进行扩展改进得到。可以说随着越来越多的应用转向iOS7,自定义VC切换将成为新的用户交互实现的基础和重要部分,对于今后会在其基础上会衍生出怎样让人眼前一亮的交互设计,不妨让我们拭目以待(或者自己努力去创造)。


3.4 ios5中UIViewControlleraddChildViewController等新方法

http://justsee.iteye.com/blog/1829687


多个viewcontroller放置在一起,手势处理不能响应

http://bbs.csdn.net/topics/390335079


4 参考链接

VCTransitionDemo

https://github.com/onevcat/VCTransitionDemo


WWDC 2013 Session笔记--iOS7中的ViewController切换

http://www.cocoachina.com/industry/20131017/7187.html


VCTransitionsLibrary

https://github.com/ColinEberhardt/VCTransitionsLibrary


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

http://www.itnose.net/detail/6107362.html


iOS开发之PresentViewController Modally --弹出模态ViewController

http://blog.csdn.net/why_ios/article/details/7837227


Multiple view controllers on screen at once?

http://stackoverflow.com/questions/2423858/multiple-view-controllers-on-screen-at-once

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

推荐阅读更多精彩内容