Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

viewWillAppear called twice with custom segue animation using transitionFromView

I'm looking to perform a segue to replace the window's root view controller by another view controller using a curl up animation. The idea is that I have a SplashViewController being displayed for a couple of seconds before transitioning (performSegueWithIdentifier:) to the next one, LoginViewController, using the curl up animation.

I've created a custom UIStoryboardSegue class called AnimatedSegue. Here is the code of the overridden perform method:

- (void)perform
{
  UIViewController *source = self.sourceViewController;
  UIViewController *destination = self.destinationViewController;

  UIWindow *window = source.view.window;

  [UIView transitionFromView:source.view
                      toView:destination.view
                    duration:1.0
                     options:UIViewAnimationOptionTransitionCurlUp
                  completion:^(BOOL finished) {
                    [window setRootViewController:destination];
                  }];
}

It works fine except that in iOS 6 (apparently not in iOS 5) the viewWillAppear: method is being called twice on the destination view controller. It seems that it's called a first time during the transition and second time when it executes [window setRootViewController:destination];

Note that I don't want to use a navigation controller. The SplashViewController gets deallocated (as expected) once the transition is over.

Any ideas on how to fix my problem?

like image 406
Sylvain Guillopé Avatar asked Oct 17 '12 18:10

Sylvain Guillopé


3 Answers

Answering to my own question in case it can help someone else...

I ended up using Core Animation's CATransition class to create the segue's animation instead of the UIView's animation methods.

Here's how my new perform method looks like:

- (void)perform
{
  UIViewController *source = self.sourceViewController;
  UIWindow *window = source.view.window;

  CATransition *transition = [CATransition animation];
  [transition setDuration:1.0];
  [transition setDelegate:self];
  [transition setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
  [transition setType:@"pageCurl"];
  [transition setSubtype:kCATransitionFromBottom];

  [window.layer addAnimation:transition forKey:kCATransition];
  [window setRootViewController:self.destinationViewController];
}
like image 174
Sylvain Guillopé Avatar answered Nov 15 '22 23:11

Sylvain Guillopé


I would probably do this in a different way, using 2 views and one controller, rather than 2 controllers with a custom segue. In your storyboard, just have a blank view for the controller, and add two xib files (of the view type) with the splash view and a main view. The splash view would be added as a subview of the controller's view in viewDidLoad, and then switched out using the same method you did:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.splashView = [[NSBundle mainBundle] loadNibNamed:@"SplashView" owner:self options:nil].lastObject;
    [self.view addSubview:self.splashView];
    [self performSelector:@selector(removeSplash) withObject:nil afterDelay:2];
}

-(void)removeSplash {
    self.mainView = [[NSBundle mainBundle] loadNibNamed:@"MainView" owner:self options:nil].lastObject;
    [UIView transitionFromView: self.splashView toView:self.mainView duration:.6 options:UIViewAnimationOptionTransitionCurlUp
                    completion:nil];
}
like image 25
rdelmar Avatar answered Nov 16 '22 00:11

rdelmar


If you really, really want to use transitionFromView: the only thing I could find that works is to just create a screen shot of the source view controller, and animate that.

- (void)perform
{
    UIViewController *source = self.sourceViewController;
    UIViewController *destination = self.destinationViewController;

    // Create screenshot for animation
    UIGraphicsBeginImageContextWithOptions(source.view.bounds.size, NO, 0.0);
    CGContextRef context = UIGraphicsGetCurrentContext();
    [source.view.layer renderInContext:context];
    UIImageView *screenShot = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
    UIGraphicsEndImageContext();

    // Put destination view controller and screen shot in place
    UIWindow *window = source.view.window;
    window.rootViewController = destination;
    [window addSubview:screenShot];

    [UIView transitionFromView:screenShot
                        toView:destination.view
                      duration:1.0
                       options:UIViewAnimationOptionTransitionCurlUp
                    completion:^(BOOL finished) {
                    }];
}

viewWillAppear: and its ilk only get called once as expected.

like image 38
Skotch Avatar answered Nov 16 '22 00:11

Skotch