Snacks
Snacks

Reputation: 513

Going to a ViewController with a custom segue causes viewWillAppear to get called twice

I have written a custom segue, because i wanted to add my own animations to it. Everything works alright, besides the fact that the viewDidLoad method in the target view CiewController gets called twice. Here is the perform method for my segue:

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

    [sourceViewController.view addSubview:destinationViewController.view];

    CGPoint originalCenter = destinationViewController.view.center;
    destinationViewController.view.center = CGPointMake(self.originatingPoint.x * 3, self.originatingPoint.y);

    [UIView animateWithDuration:0.25
        delay:0.0
        options:UIViewAnimationOptionCurveEaseInOut
        animations:^{
            destinationViewController.view.center = originalCenter;
        }
        completion:^(BOOL finished) {
            [sourceViewController presentViewController:destinationViewController animated:NO completion:NULL]; // present VC
        }];
}

Does anyone have any idea what could cause this?

[----- EDIT -----]

The segue is present in the storyboard as a custom segue with a segue class that I have written myself. The only thing that is different in my class is the perform method which I have put above. The segue is called through a button, and the prepareForSegue method is called only once.

[----- EDIT 2 -----]

I checked the viewDidLoad method of the targetVC and it is only called once per segue. Nonentheless, it would be much more convenient for me to use viewWillAppear, so do you maybe know a different way in which i can do this animation?

Upvotes: 2

Views: 344

Answers (1)

pronebird
pronebird

Reputation: 12260

I suggest you to use UIViewControllerTransitioningDelegate and UIViewControllerAnimatedTransitioning which are the more appropriate conventions to use for transitions since iOS 7.0. Since iOS 8.0 you also gain UIPresentationController support which allows you to build even richer transitions.

Example:

@interface ModalTransitionAnimator : NSObject<UIViewControllerAnimatedTransitioning>

@property (nonatomic) CGPoint originatingPoint;

@end

@implementation ModalTransitionAnimator

- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
    return 0.25;
}

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
    NSTimeInterval duration = [self transitionDuration:transitionContext];
    UIView* sourceView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    UIView* destinationView = [transitionContext viewForKey:UITransitionContextToViewKey];
    UIView* container = transitionContext.containerView;

    [container addSubview:destinationView];

    CGPoint originalCenter = destinationView.center;

    destinationView.center = CGPointMake(self.originatingPoint.x * 3, self.originatingPoint.y);

    [UIView animateWithDuration:duration animations:^{
            destinationView.center = originalCenter;
        }
        completion:^(BOOL finished) {
            [transitionContext completeTransition:YES];
        }];
}

@end

Then in prepareForSegue you simply assign transitioning delegate and implement UIViewControllerTransitioningDelegate to return appropriate animators for presentation or dismissal.

@interface ViewController : UIViewController<UIViewControllerTransitioningDelegate>
@end

@implementation ViewController

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    UIViewController* controller = (UIViewController*)segue.destinationViewController;

    controller.transitioningDelegate = self;
    controller.modalPresentationStyle = UIModalPresentationCustom;
    controller.modalPresentationCapturesStatusBarAppearance = YES;
}

- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
    ModalTransitionAnimator *animator = [[ModalTransitionAnimator alloc] init];
    animator.originatingPoint = /* ... */;

    return animator;
}

@end

Since this is a modal transition, you have to use presentViewController:animated: when presenting controllers with it. Therefore use normal "show" segues in Storyboards and they will automatically run all animations under the hood, no need to reinvent segues here.

I had example of how to build custom transitions somewhere on Github:

https://github.com/pronebird/CustomModalTransition/tree/ios8

Upvotes: 4

Related Questions