2

I am presenting my custom UIViewController (called "temp") with a custom animation. The UIVC gets called with:

[temp setModalPresentationStyle:UIModalPresentationCustom];
temp.transitioningDelegate = self;
[temp.view setHidden:YES];
[self presentViewController:temp animated:YES completion:nil];

My custom animation is presenting a view modally from right to top-left position of the screen. It is being presented hidden so the user doesn't see the animation. After it reaches the SCREEN_HEIGHT (768) position it is being set to visible and animated (moved) from top to bottom being presented in the middle. The goal was to present a view from top to bottom and dismiss it from top to bottom (like a movie scene). This code is the NOT working one:

- (void)animateTransition:(id)transitionContext
{
    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    NSLog(@" fromViewController %@ ",fromViewController);
    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    NSLog(@" toViewController %@ ",toViewController);
    UIView *containerView = [transitionContext containerView];
    if (self.presenting)
    {
        // set starting rect for animation toViewController.view.frame = [self rectForDismissedState:transitionContext];
        [containerView addSubview:toViewController.view];
        [UIView animateWithDuration:0.5
                         animations:^{

                             toViewController.view.frame = CGRectMake(-self.customSize.width, self.yValue, self.customSize.width, self.customSize.height);
                         }
                         completion:^(BOOL finished)
         {
             //HERE IS THE PROBLEM!!!
             [toViewController.view setHidden:NO];
             [UIView animateWithDuration:[self transitionDuration:transitionContext]
                              animations:^{
                                  CGRect variable = [self rectForPresentedState:transitionContext];
                                  CGRect fitToCurrentScreenResolution = CGRectMake(0, 0, variable.size.width, variable.size.height);
                                  toViewController.view.frame = fitToCurrentScreenResolution;

                              }
                              completion:^(BOOL finished)
              {
                  [transitionContext completeTransition:YES];
              }];
        }];
    }
    else
    {
        [UIView animateWithDuration:[self transitionDuration:transitionContext]
                         animations:^{
                             fromViewController.view.frame = [self rectForDismissedState:transitionContext];
                         }
                         completion:^(BOOL finished)
         {
             [transitionContext completeTransition:YES];
             [fromViewController.view removeFromSuperview];
         }
         ];
    }
}

And here is the solution:

- (void)animateTransition:(id)transitionContext
{
    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    NSLog(@" fromViewController %@ ",fromViewController);
    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    NSLog(@" toViewController %@ ",toViewController);
    UIView *containerView = [transitionContext containerView];
    if (self.presenting)
    {
        // set starting rect for animation toViewController.view.frame = [self rectForDismissedState:transitionContext];
        [containerView addSubview:toViewController.view];
        [UIView animateWithDuration:0.5
                         animations:^{

                             toViewController.view.frame = CGRectMake(-self.customSize.width, self.yValue, self.customSize.width, self.customSize.height);
                         }
         ];

        [toViewController.view setHidden:NO];
        [UIView animateWithDuration:[self transitionDuration:transitionContext]
                         animations:^{
                             CGRect variable = [self rectForPresentedState:transitionContext];
                             CGRect fitToCurrentScreenResolution = CGRectMake(0, 0, variable.size.width, variable.size.height);
                             toViewController.view.frame = fitToCurrentScreenResolution;

                         }
                         completion:^(BOOL finished)
         {
             [transitionContext completeTransition:YES];
         }];
    }
    else
    {
        [UIView animateWithDuration:[self transitionDuration:transitionContext]
                         animations:^{
                             fromViewController.view.frame = [self rectForDismissedState:transitionContext];
                         }
                         completion:^(BOOL finished)
         {
             [transitionContext completeTransition:YES];
             [fromViewController.view removeFromSuperview];
         }
         ];
    }
}

My question is simple. Why is my UIVC being presented twice?

I could have used a hack but I wouldn't find out why it is happening. So far it seems that when the animation enters the completion BLOCK it calls the view again.

The apple docs say:

A block object to be executed when the animation sequence ends. This block has no return value and takes a single Boolean argument that indicates whether or not the animations actually finished before the completion handler was called. If the duration of the animation is 0, this block is performed at the beginning of the next run loop cycle. This parameter may be NULL.

Is the view being drawn again since the next run loop cycle is being started? NOTE: Even thought the view is being presented twice, the viewDidLoad method is being called only once.

I would like to know why this is happening. There are some stackoverflow questions with the same code but with different usage scenarios having the same problem without a working solution or explanation.

Thank you for any advice/comment.

Community
  • 1
  • 1
MB_iOSDeveloper
  • 4,178
  • 4
  • 24
  • 36
  • Hey just checking in! Did you ever find a solution to this? I just ended a 4 day struggle with a similar problem. Is the code you posted "...and here is the solution" actual code that solved your problem? – serge-k Oct 16 '14 at 10:27
  • 1
    Yes the code worked fine in iOS7, but did not for iOS8. It seems that chained animations like the not working code example produce problems when a custom transition occurrs. I think it happens because of the custom transition code example that most people use. There somewhere the view is apearing twice. The solution that worked for both was adding the SKView in viewDidLoad , since it loads only once. That did the trick for me in the end. – MB_iOSDeveloper Oct 16 '14 at 13:01
  • @banana_developer_4_iDrioid, thanks for clarifying so quickly! I am glad to hear you found a work around, it would be nice to drill down and figure out the exact culprit... I'll post my situation, perhaps someone else could tie the two together... – serge-k Oct 16 '14 at 19:49

1 Answers1

0

iOS 8.0, Xcode 6.0.1, ARC enabled

Yeah you are definetely onto it with "chained animation" (see comment from O.P.).

I witnessed a similar problem trying to hide and show the UIStatusBar for various UIViewControllers in my application, e.g. I have a dummy after load screen UIViewController that shows the same image as the load screen, but it has some added animations.

I am using a custom transition, which features a UIViewController that handles the transition from the "from" UIViewController and the "to" UIViewController by adding or removing their views from itself and assigning the "to" UIViewController "control" to itself. So on and so forth.

In the app. delegate,

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;

I had to instantiate the "initial view controller" and then initialize the transition UIViewController with it. Since there are no "to" UIViewControllers the transition UIViewController must hold an initial UIView of the UIViewController it was initialized with until a transition is triggered.

This was done utilizing,

self.window.rootViewController = self.transitionViewController;
[self.window makeKeyAndVisible];

After the very first transition, there always two UIViews overlaid onto each other. And two UIViewControllers one existing as the current control for the transition UIViewController that was assigned during the transition and the previous UIViewController that remains until the transition completes.

This was the code I was trying to use to show/hide the UIStatusBar, one must have the "View controller-based status bar appearance" set to "YES" in the *-Info.plist file.

- (void)viewDidLoad
{
    [self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)];
}

- (BOOL)prefersStatusBarHidden
{
    return false;
}

Whenever the "return" value was changes from default "false" to "true" regardless of when

[self performSelector:@selector(setNeedsStatusBarAppearanceUpdate)];

was triggered, delay, no delay, conditional, etc.; both UIViewControllers, the "to" and "from" were reloaded. At first this was not noticeable, however after implementing an NSURLSession in one of the UIViewControllers that was triggered in the - (void)ViewDidLoad; the problem was clear. The session was executed twice and the graphical content involved was also updated.

I successfully solved the issue in two ways, however I kept the 2nd.

  1. I put everything in -(void)ViewDidLoad; in an if statement and forced it to only be executed once, using an instance variable boolean. The -(void)ViewDidLoad; still loaded twice, however, things that I did not want to execute twice did not.
  2. I transitioned to the UIViewController at which the UIStatusBar hidden state needed to change without using my transitional UIViewController. After the UIStatusBar was shown or hidden, I would reset the "rootViewController" for the app. delegate, once again assigning the transitional UIViewController as always "shown".

I describe how to do this in the following post: https://stackoverflow.com/a/26403108/4018041

Thanks. Hope this helps someone. Please comment on how this could be handled in the future for either the OP or myself.

Community
  • 1
  • 1
serge-k
  • 3,394
  • 2
  • 24
  • 55