views:

282

answers:

2

Hello! I have an MVC application with a single model and several views (something like skins). I want the user to be able to switch the views and I can’t get it working with interface orientation. The most simple approach looks like this:

- (void) switchToADifferentView: (UIView*) newView {
    // self is a descendant of UIViewController
    self.view = newView;
}

This does not work because the incoming view does not get rotated according to current orientation (until the next orientation change, test case). Is there a way to force the orientation on a view? It looks like the system is trying really hard to keep the interface controls for itself. (Or is it as simple as setting the right transform by hand?)

I figured I’d better not switch the views directly and switch controllers instead. This makes sense, as it makes the initial code simpler. But how do I switch controllers that have no “navigation relation” between them? I guess I could use presentModalViewController:, but that seems like a hack. Same goes for navigation controller. If I exchange the controllers by hand, I get the wrong orientation again:

- (void) switchToAController: (id) incoming {
    [currentController.view removeFromSuperview];
    [window addSubview:incoming.view]; // does not respect current orientation
}

Now how the heck do I simply exchange the current controller for another one? Again, the controllers are something like “skins” operating above a shared model, so it really makes no sense to pretend that skin A is a “modal” dialog above skin B or that they’re a part of a navigation stack.

+1  A: 

When switching views directly using the first code sample the orientation can be fixed by setting the transform by hand:

- (CGAffineTransform) transformForOrientation: (UIInterfaceOrientation) io
{
    NSParameterAssert(io <= 4);
    // unknown, portrait, portrait u/d, landscape L, landscape R
    float angles[] = {0, 0, M_PI, M_PI/2, -M_PI/2};
    return CGAffineTransformMakeRotation(angles[io]);
}

- (void) switchToView: (UIView*) newView
{
    newView.transform = [self transformForOrientation:self.interfaceOrientation];
    self.view = newView;
}

Also, if you want to exchange the views during rotation, things get even more complicated. First you have to transform the incoming view according to the current one. This has to be done in the willRotate… callback:

- (void) willRotateToInterfaceOrientation: (UIInterfaceOrientation) target
         duration: (NSTimeInterval) duration
{
    incomingView = [[UIView alloc] init…];
    incomingView.transform = self.view.transform;
}

And then you switch the views in the willAnimate… callback and set the transform according to the target orientation:

- (void) willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation) io
         duration: (NSTimeInterval) duration
{
    incomingView.transform = [self transformForOrientation:io];
    self.view = incomingView;
}

This way the new view replaces the current one and rotates smoothly to the target position.

zoul
Is there any ref docs on the need to transform the view orientation manually?
Seymour Cakes
A: 

In the end I went with a “trampoline” approach. Each “skin” is a separate controller and there is a special root controller that presents the skins as modal controllers. When the skin wants to switch to a different one, it sets a flag indicating the desired next skin and dismisses itself. The root controller wakes up (viewWillAppear), notices the flag and displays the next skin.

This solution has two main advantages: 1] The controller code stays simple, as each controller displays one and only view, no view switching. 2] There is no need to hack the orientation code, because view orientation inside modally displayed controllers is handled transparently by the system.

zoul