Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is transitionFromViewController just a convenience method in iOS?

I'm not sure I understand what transitionFromViewController:toViewController:duration:options:animation:completion: does exactly. Is it just a convenience method?

For instance, what's the difference between doing this...

[self transitionFromViewController:fromViewController
                  toViewController:toViewController
                          duration:0.25
                           options:UIViewAnimationOptionCurveEaseInOut
                        animations:^{
                            fromViewController.view.alpha = 0;
                            toViewController.view.alpha = 1;
                        } completion:^(BOOL finished) {
                            [fromViewController removeFromParentViewController];
                            [toViewController didMoveToParentViewController:self];
                        }];

...and this?

[self.view addSubview:toViewController.view];
[UIView animateWithDuration:0.25
                      delay:0
                    options:UIViewAnimationOptionCurveEaseOut
                 animations:^{
                     fromViewController.view.alpha = 0;
                     toViewController.view.alpha = 1;
                 } completion:^(BOOL finished){
                     [fromViewController.view removeFromSuperview];
                     [fromViewController removeFromParentViewController];
                     [toViewController didMoveToParentViewController:self];
                 }];

The reason I ask is that in some cases I need to add the child controller view to a specific subview of the container controller view. Using transitionFromViewController:toViewController:duration:options:animation:completion: does not give me this option.

like image 837
hpique Avatar asked Nov 16 '12 23:11

hpique


2 Answers

Yes, I think you're right: They appear to be functionally the same (not sure we can call it a convenience method without knowing the details of the implementation, but could well be). Obviously, transitionFromViewController is designed for view controller containment and animateWithDuration is designed a general purpose animation of views.

Given that you're obviously doing containment, one probably should use transitionFromViewController rather than animateWithDuration. It's unambiguous and the technique recommended by Apple. And if you have the fromViewController.view in a subview, the new toViewController.view will be added to the same subview.

I'd also suggest including the missing willMoveToParentViewController and addChildViewController (which I assume you omitted for the sake of brevity, but I include for the sake of completeness):

[fromViewController willMoveToParentViewController:nil];
[self addChildViewController:toViewController];

[self transitionFromViewController:fromViewController
                  toViewController:toViewController
                          duration:0.25
                           options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionCrossDissolve
                        animations:^{}
                        completion:^(BOOL finished){
                            [fromViewController removeFromParentViewController];
                            [toViewController didMoveToParentViewController:self];
                        }];

Also note that I'm using UIViewAnimationOptionTransitionCrossDissolve. If you're setting alphas manually, don't forget to initialize the toViewController.view.alpha, e.g.:

[fromViewController willMoveToParentViewController:nil];
[self addChildViewController:toViewController];
toViewController.view.alpha = 0.0;

[self transitionFromViewController:fromViewController
                  toViewController:toViewController
                          duration:0.25
                           options:UIViewAnimationOptionCurveEaseInOut
                        animations:{
                            fromViewController.view.alpha = 0.0;
                            toViewController.view.alpha = 1.0;
                        }
                        completion:^(BOOL finished){
                            [fromViewController removeFromParentViewController];
                            [toViewController didMoveToParentViewController:self];
                        }];
like image 150
Rob Avatar answered Oct 18 '22 11:10

Rob


Short answer:
No it's not just a convenience method as the time when what appearance-method is called on both child-controllers is different.

Long answer:
Regarding -->

[self.view addSubview:toViewController.view];
[UIView animateWithDuration:0.25
                      delay:0
                    options:UIViewAnimationOptionCurveEaseOut
                 animations:^{
                     fromViewController.view.alpha = 0;
                     toViewController.view.alpha = 1;
                 } completion:^(BOOL finished){
                     [fromViewController.view removeFromSuperview];
                     [fromViewController removeFromParentViewController];
                     [toViewController didMoveToParentViewController:self];
                 }];

What happens behind the scene when you call

[self.view addSubview:toViewController.view];

is something like this (of course this is not complete)

[toViewController.view viewWillAppear:false]; <-- meaning not animated
//Add the view to the view hierarchy
//Layout the view
[toViewController.view viewDidAppear:false]; <-- meaning not animated

Afterwards when you do the animation toViewController "thinks" that it's view is already visible and has been presented without an animation. The same is true for the fromViewController. During your animation the fromViewController does not know that it's view gets dismissed as viewWill/DidDisappear will be called after the animation with your call to

[fromViewController.view removeFromSuperview];


On the other hand -->

[self transitionFromViewController:fromViewController
              toViewController:toViewController
                      duration:0.25
                       options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionTransitionCrossDissolve
                    animations:^{}
                    completion:^(BOOL finished){
                        [fromViewController removeFromParentViewController];
                        [toViewController didMoveToParentViewController:self];
                    }];

does something like this

[UIView animateWithDuration:yourDuration
                      delay:yourDelay
                    options:yourOptions
                 animations:^{
                     [toViewController viewWillAppear:true];
                     [fromViewController viewWillDisappear:true];
                     //Add toViewcontroller.view to the view hierarchy
                     //Layout the toViewcontroller.view --> changes here are not animated. (There is no "previous state" as the view has just been added to the hierarchy)
                     //Call your animation-block
                 } completion:^(BOOL finished){
                     [toViewController viewDidAppear:true];
                     [fromViewController.view removeFromSuperview];
                     [fromViewController viewDidDisappear:true];
                     //Call your completion-block
                 }];

So long answer short:
viewWillAppear and viewWillDisappear are called inside the animation-block and get passed true. This may or may not be useful to your application so there may or may not be a difference between the two methods.

like image 3
Xatian Avatar answered Oct 18 '22 12:10

Xatian