Switching between various uiviewcontrollers in ios7

Source: Internet
Author: User

 

 

There is no doubt that ViewController (VC in this article) is the most important class for building a Cocoa or CocoaTouch program using MVC, in our daily work, the most time and effort is to write code for the VC part. Apple products focus on user experience, and pondering the details is Apple's constant demand and hope for developers. In the user experience, the relationship between VC, such as the Migration and conversion of animation effects between different VC has always been a key point worth further consideration. In iOS7, Apple provided a complete solution for migrating between VC production, which can be said to point out a unified recommendation path for different implementation solutions.

VC switching solution before iOS 7 SDK

Before going deep into the implementation of the new API for VC switching in iOS 7, let's review the general practices. This helps to understand why iOS 7 switches VC to a new solution. If you are familiar with the VC container introduced in iOS 5, you can skip this section.

In iOS5 and iOS6, in addition to standard Push, Tab, and PresentModal, ChildViewController is generally used to complete the transition between VC switches. ChildViewController and custom Controller containers are added to iOS 5 SDK and can be used to generate custom VC containers. In simple terms, a typical usage is similar to this:

//ContainerVC.m[self addChildViewController:toVC];[fromVC willMoveToParentViewController:nil];[self.view addSubview:toVC.view];__weak id weakSelf = self;  [self transitionFromViewController:fromVC                  toViewController:toVC duration:0.3                           options:UIViewAnimationOptionTransitionCrossDissolve                        animations:^{}                        completion:^(BOOL finished) {    [fromVC.view removeFromSuperView];    [fromVC removeFromParentViewController];    [toVC didMoveToParentViewController:weakSelf];}];

When you manage the view, you can use the Animation block of transitionFromViewController: toViewController:... to implement some simple switching effects. The misuse of UIViewController I wrote at the beginning of last year pointed out that[viewController.view addSubview:someOtherViewController.view];The existence of such code is generally misuse of VC. This conclusion applies to non-Controller containers. For custom Controller containers, it is correct to add other VC views to the current view (of course, you cannot forget to pass the VCaddChildViewController:Method ).

The main purpose of the VC container is to solve the need to add different VC to the same screen and provide some simple custom switching effects. By using the VC container, you can make the view relationship correct so that the added VC can correctly receive VC events such as screen rotation and viewDidLoad, and then perform corresponding operations correctly. The VC container can solve some problems, but we should also see that there are still many shortcomings in the Custom switching effect. First, the code is highly coupled, and the code of VC switching is directly written in the iner, which is difficult to separate and reuse. Second, the switching effect is limited, and only UIView animation can be used for switching, management is also a little troublesome. IOS 7 provides a new set of custom VC switches to address these two problems.

IOS 7 custom ViewController animation switching main APIs related to Custom Animation Switching

Before going deeper, let's take a look at the interfaces related to this part of the new SDK, their relationships, and typical usage. The names of these interfaces and classes are similar, but they can better describe their respective functions. They may be confusing at the beginning, but when you implement one or two examples by yourself, the relationship between them will be gradually clarified. (The related content is defined in UIViewControllerTransitioning. h of UIKit)

@ Protocol UIViewControllerContextTransitioning

This interface is used to provide the switch context for developers to use. It contains various information such as the VC to which the switch belongs, and generally does not need to be implemented by developers themselves. Specifically, one of the purposes of iOS7 custom switching is to decouple the Code related to switching. When performing VC switching, you must switch the VC information before and after the implementation of the switching effect, the system provides an object that implements this interface in the comparison of newly added APIs for our use.

For the animation implementation of switching (here we will introduce a simple animation, and I will introduce a gesture-driven animation later), the most important methods of this interface are:

  • -(UIView *) containerView; When VC switches to the view container, the developer should remove the cut-out view and add the cut-in view to the view container.
  • -(UIViewController *) viewControllerForKey :( NSString *) key; provides a key and returns the corresponding VC. In the current SDK, only UITransitionContextFromViewControllerKey and UITransitionContextToViewControllerKey are selected, indicating the VC to be cut out and cut in respectively.
  • -(CGRect) initialFrameForViewController :( UIViewController *) vc; the initial position of a VC, which can be used for animation computation.
  • -(CGRect) finalFrameForViewController :( UIViewController *) vc; corresponding to the above method, get the frame that a VC should be in at the end of the switch.
  • -(Void) completeTransition :( BOOL) didComplete; report to this context that the switchover has been completed. @ Protocol UIViewControllerAnimatedTransitioning

    This interface is responsible for the specific content of the switchover, that is, "What should happen during the switchover ". Most of the code used by developers to implement the custom switching effect is used to implement this interface. There are only two methods that need to be implemented:

    • -(NSTimeInterval) transitionDuration :( id <UIViewControllerContextTransitioning>) transitionContext; the system provides a switching context, we will return the time required for this switchover Based on the context (generally, it is enough to return the animation time. the SDK will use this time to calculate frames during the percentage-driven switchover, ).

    • -(Void) animateTransition :( id <UIViewControllerContextTransitioning>) transitionContext; this method is called during the switchover. The settings and animations of the UIView during the switchover are completed in this method.

      @ Protocol UIViewControllerTransitioningDelegate

      The function of this interface is simple and simple. When VC switching is required, the system will ask whether to use a custom switching effect like an object implementing this interface. This interface has four similar methods:

      • -(Id <UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController :( UIViewController *) presented presentingController :( UIViewController *) presenting sourceController :( UIViewController *) source;

      • -(Id <UIViewControllerAnimatedTransitioning>) animationControllerForDismissedController :( UIViewController *) dismissed;

      • -(Id <UIViewControllerInteractiveTransitioning>) interactionControllerForPresentation :( id <UIViewControllerAnimatedTransitioning>) animator;

      • -(Id <UIViewControllerInteractiveTransitioning>) interactionControllerForDismissal :( id <UIViewControllerAnimatedTransitioning>) animator;

        The first two methods are for animation switching. We need to provide an object that implements the UIViewControllerAnimatedTransitioning interface when presenting VC and disband VC respectively (including the switching duration and how to switch between them ). The last two methods involve interactive switching.

        Demo

        In that case, the explanation in line 1 is not as good as a simple Demo, so... it's demo time ~ The code of the entire demo is displayed on the github page. If you need it, you can refer to this article.

        We plan to implement a simple and custom modalViewController switchover. The effects of ordinary present modal VC are already very familiar to everyone. This time we will first implement a custom similar modal present effect. What is different from the general effect is that, we hope that the appearance of modalVC will not be as boring as it simply appears from the bottom, but with an elastic effect (although it is flexible here, it only refers to the use of the simulated animation of UIView, without designing another important feature of iOS 7, UIKit Dynamics. The use of UIKit Dynamics may certainly achieve more vivid and gorgeous effects, but it is beyond the scope of this topic, so it is not here. For more information about UIKit Dynamics, see my previous introduction to this topic ). Let's first implement a simple ModalVC pop-up .. This section is very basic and I will explain the background. Skip the code segment if you are not a beginner ..

        First define a ModalVC and the corresponding protocal and delegate methods:

        //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];    }}
                 
                

        This is a standard implementation of modalViewController. In practice, some users prefer to directly send dismissViewController methods to self in-buttonClicked. In the current SDK, if the current VC is displayed, the message will be directly forwarded to the VC that displays it. However, this is not a good implementation. It violates the philosophy of programming and is easy to fall into the trap. For specific cases, refer to the comments in this article.

        Therefore, we use a standard method to present and disband this 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];}

        Test it. No problem. Then we can start to implement custom switching. First, we need an object that implements UIViewControllerAnimatedTransitioning... Well, create a class to implement it, such as 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];                     }];}
                  
                 
                

        Explain this implementation:

        1. First, we need to get the information of the two viewcontrollers involved in the switchover, and use the context method to get their reference;
        2. For the VC to be presented, we want it to appear below the screen, so we set the initial position to the bottom edge of the screen;
        3. Add view to containerView;
        4. Start animation. The animation duration here is the same as the switching duration, and it is 0.8 s. The UIView animation API of usingSpringWithDamping is newly added by iOS7. It describes an animation curve Simulating Spring Action. We only use it here. For more information, see the relevant documentation; (By The Way, iOS7 adds a convenient Category and UIViewKeyframeAnimations to the UIView animation. You can use this method to add a Key Frame Animation for the UIView animation)
        5. After the animation ends, we must report to the context whether the VC switch is complete and whether the switch is successful (there is no possibility of failure in the animation switch here, so pass a YES directly ). After receiving the message, the system maintains the VC status.

          Next we will implement a UIViewControllerTransitioningDelegate, which should be able to work. To put it simply, it is better to directly implement this interface in MainViewController. Declare and implement this interface in MainVC, and then add or change it to the following code:

          @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. Run it and you can get the following results:

          Percent switch of gesture-driven

          IOS7 introduces a gesture-driven VC switching method (Interactive switching ). If you use various applications of the system and push a new VC in navViewController, you do not need to click Back in the upper left corner to return the result, instead, you can complete the return operation by selecting to the right from the left side of the screen. During this operation, we can even cancel our gesture to cancel this VC Transfer. In the new version of Safari, we can even use the same gesture to complete the page's rewind function (so the toolbar at the bottom of the screen is largely a decoration ). If you do not know or have not paid too much attention to this change, you may as well take the iOS 7 team at hand to try it now. Friends who visit the mobile phone will remember to switch it back :)

          Let's implement this function in our own VC switch. First, we need to add something based on the previous knowledge:

          The first is UIViewControllerContextTransitioning. This is the VC switching context provided by the system. If you have read the header file description in depth, you should find three methods about InteractiveTransition, it is used to handle interactive switching. However, in actual use, we can ignore them. Instead, we can use the iOS 7 SDK to convert the ready-made class to interactive switch and add a new class: UIPercentDrivenInteractiveTransition.

          What is UIPercentDrivenInteractiveTransition?

          This is a class that implements the UIViewControllerInteractiveTransitioning interface. It provides a series of convenient methods for us to implement and control the interactive switching process with a percentage. In general, we will use some gestures to perform interactive transfer (of course, use other input for advanced features .. for example, the voice, iBeacon distance, or even face smile are not an input driver. After all, there is no limit to imagination ..), this makes it very convenient to use this class (generally its subclass. In gesture recognition, we only need to tell the current status percentage of the instance of this class, and the system calculates the UI rendering for US based on this percentage and the previously set migration method, very convenient. Several important methods:

          • -(Void) updateInteractiveTransition :( CGFloat) percentComplete update percentage. Generally, a value is calculated based on the length of Gesture Recognition and then updated. The detailed usage will be shown in the following example.
          • -(Void) cancelInteractiveTransition reports canceling interaction and returns the status before switching.
          • -(Void) finishInteractiveTransition report interaction is complete, updated to the switched status @ protocol UIViewControllerInteractiveTransitioning

            As mentioned above, UIPercentDrivenInteractiveTransition is only a class that implements this interface. To implement the interactive switching function, we need to implement this interface. Most of the time, we don't need to implement this interface on our own, so we won't explain it in this entry. If you are interested, you can study it on your own.

            In addition, the methods for returning Interactive implementation objects in the UIViewControllerTransitioningDelegate mentioned above will also be used in Interactive switching.

            Continue Demo

            Demo time again. On the basis of the demo just now, this time we use an upward movement gesture to show the previously presented ModalViewController to dismiss ~ Of course, it is an interactive switch that can be canceled halfway.

            First, create a new class that inherits from UIPercentDrivenInteractiveTransition. This saves us a lot of trouble.

            //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

            It is a little long, but it is relatively simple to do.

            1. We set a BOOL variable to indicate whether the switch is in progress. This Boolean value will be set when the gesture is detected and will be used when InteractiveTransition is returned.
            2. Calculate the percentage. We set the downward movement of 400 pixels or above to 100%. Each time the gesture status changes, the new percentage is calculated based on the current gesture position. The result is limited to 0 ~ Between 1. Then update the percentage of InteractiveTransition.
            3. At the end of the gesture, set the mark being switched back to NO and then judge. In step 2, if we set the gesture distance to more than half the set distance, we think that the gesture should be ended. Otherwise, we should return the original state. It is used here to determine whether the transition should end.

              Next, we need to add a downward movement UIView animation to show dismiss. This is very simple and similar to BouncePresentAnimation. Write a NormalDismissAnimation to implement the class of the UIViewControllerAnimatedTransitioning interface. If you are interested, you can view the source code on your own.

              Finally, adjust the content of MainViewController. The main modification points are as follows:

              //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. Animation and interaction Switching Controller when dismiss is added
              2. When initializing modalVC, bind VC to the Controller of the interactive switch.
              3. Delegate methods when implementing dismiss for UIViewControllerTransitioningDelegate, including returning corresponding animations and switching between controllers

                Finished. If you move down, the effect is as follows:

                Summary of custom VC switching in iOS 7

                The demo only shows the custom switching effect on the present and dismiss of modalVC. Of course, there is also a set of methods corresponding to the Push and Pop switching of the Navigation Controller. The implementation is very similar to dismiss, except that the animation and interaction method corresponding to UIViewControllerTransitioningDelegate is changed to UINavigationControllerDelegate (to distinguish between push and pop, check this interface as soon as possible ). Another good benefit is that for standard navController Pop operations, Apple has implemented a gesture-driven return for us, so we don't have to worry about it every time. cheers ~

                In addition, you may think it is convenient to use the transition animation method provided by the VC container for VC switching. Why do I need to introduce a set of custom methods in iOS7. In fact, they basically undertake two completely different types of tasks: Custom VC containers can provide their own VC structure, it also ensures that various methods and notifications of the system can be accurately transmitted to the appropriate VC. The transition method provided by the system can implement some simple UIView animations, but it is difficult to reuse them, it can be said that it is completely coupled with containerVC. Custom switching does not change the VC's organizational structure, but is only responsible for providing the view effect, because VC switches the animation part and the animation driver part all use the interface, the reusability is very good. In most cases, a set of elaborate UIView animations can be easily used in different VC or even different projects.

                Note that VCTransitionsLibrary of ColinEberhardt on Github has provided us with a series of VC custom switching animations, it is thanks to the good design of iOS7 (although the names of these interfaces are similar, there will be some confusing before it is understood) that these effects are very convenient to use, I believe that general projects are sufficient. Other more complex or dazzling effects can also be improved based on the extension. As more and more applications turn to iOS7, custom VC switching will become the basis and important part of new user interaction implementation, let's wait and see (or try to create it by ourselves) for the future interaction design that will be derived from it ).

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.