wrong orientation on UIViewController while animat

2019-04-11 12:34发布

问题:

In order to use a UISplitViewController, I'm replacing my window root controller when navigating from one view controller to the other.

In order to have some nice transition while doing so, I'm using a zooming effect like this:

MyOtherViewController *controller = [[MyOtherViewController alloc] initWithNibName:@"MyOtherView" bundle:nil];
UIWindow *window = ((MyAppDelegate *)[[UIApplication sharedApplication] delegate]).window;

controller.view.frame = [window frame];
controller.view.transform = CGAffineTransformMakeScale(0.01,0.01);
controller.view.alpha = 0;

[window addSubview:controller.view];

[UIView animateWithDuration:0.2 animations:^{
    controller.view.transform = CGAffineTransformMakeScale(1,1);
    controller.view.alpha = 1.0;
} completion:^(BOOL finished) {
    if (finished) { 
        [self.view removeFromSuperview];
         window.rootViewController = controller;
    }
}];

and this works pretty well, except that while doing the animation, the new view is always oriented as if in portrait mode, regardless of the current device orientation. When the animation is finished, the view orients itself correctly.

What am I missing?

Things I've tried:

  • putting my new controller view as the sole subview of the UIWindow
  • making my new controller the root view controller before the animation begins

A curious thing is that, if I do a recursiveDescription on the window at the beginning of my method, the window frame is defined as having a size of 768x1024 (i.e., portrait), and the view inside it of 748x1024 but with a transform of [0, -1, 1, 0, 0, 0] (does this mean a rotation or what? Shouldn't it be the identity transform?)

回答1:

UIWindow doesn't rotate. It has a rotated view inside of it (as you've seen). In this case, though, I think the problem is likely that your view has a transform on it already at this point, and you need to concatenate with it rather than replace it as you're doing in your setTransform: calls.

You shouldn't be asking the app delegate for the window, you should be getting the window from the view (self.view.window).

If at any point you're attaching your view to the window itself, rather than putting it inside the rotation view, then you'll need to know the effective transform of the view you want to match by walking the hierarchy:

- (CGAffineTransform)effectiveTransform {
    CGAffineTransform transform = [self transform];
    UIView *view = [self superview];
    while (view) {
        transform = CGAffineTransformConcat(transform, [view transform]);
        view = [view superview];
    }
    return transform;
}


回答2:

I finally figured out what was wrong. Since the frame is not a real property but a sort of calculated one, based on the values of the view bounds and the view transform, I needed to set the frame after setting the same transform as the current view, and before setting the transform again to set up the initial state of the animation. Also, the frame I need to set is the same one as the current view is currently using, as it is taking into account the window orientation (or lack thereof, as Rob Napier pointed)

So, without further ado, here's the working code:

MyOtherViewController *controller = [[MyOtherViewController alloc] initWithNibName:@"MyOtherView" bundle:nil];
UIWindow *window = [[UIApplication sharedApplication] keyWindow];

CGAffineTransform t = self.view.transform;
controller.view.transform = t;
controller.view.frame = self.view.frame;
controller.view.transform = CGAffineTransformScale(t,.01,.01);;
[window addSubview:controller.view];

controller.view.alpha = 0;

[UIView animateWithDuration:0.2 animations:^{
    controller.view.transform = t;
    controller.view.alpha = 1.0;
} completion:^(BOOL finished) {
    if (finished) { 
        [self.view removeFromSuperview];
        window.rootViewController = controller;
        [controller release];
    }
}];