-
-
Save chrishulbert/990371 to your computer and use it in GitHub Desktop.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { | |
// Firstly, we want to figure out who is the currently visible view controller. | |
// There are 4 possibilities here: | |
// 1- They could be a non-nav controller, with a visible tab (ie not under the 'more' tab) | |
// Simple: self.selectedViewController | |
// 2- They could be a nav controller, with a visible tab (ie not under the 'more' tab) | |
// Get it's visible view: self.selectedViewController.visibleViewController | |
// 3- They could be a non-nav controller, under the 'more' tab | |
// Simple: self.selectedViewController | |
// 4- They could be a nav controller, under the 'more' tab | |
// This is where it gets tricky (or buggy, really? You decide.) | |
// What UIKit does is: its sets the self.selectedViewController to your nav controller, but steals the root view and | |
// pushes it onto the self.moreNavigationController's stack | |
// So if your self.selectedViewController.viewControllers is empty, use self.moreNavigationController.visibleViewController | |
UIViewController *visible = self.selectedViewController; // For non-nav controllers | |
if ([visible respondsToSelector:@selector(visibleViewController)]) // For nav controllers | |
visible = [((UINavigationController*)visible) visibleViewController]; | |
if (!visible) // Exception for nav controllers under the more tab | |
visible = self.moreNavigationController.visibleViewController; | |
return [visible shouldAutorotateToInterfaceOrientation:interfaceOrientation]; | |
} |
OK. I started over, just using your code and your code alone.
Let's say I have view A (portrait only) and I push B onto the nav controller stack (any orientation).
At this point, if I change orientation to landscape then, when I pop the VC stack on my Nav controller, visible
ends up being UISnapshotModalViewController. My actual VC (the one being popped to) is left in the lurch, and it ends up in landscape (which is wrong). However, if I then rotate to portrait, it switches back to and remains in portrait (as it should).
The interesting (twisted?) part is that the pop animation acts AS IF it's portrait, but the view shows up in landscape. If I do another push, the animation acts as if it's in landscape (matching the actual device orientation).
UPDATE: If I use topViewController, I get the proper controller. If I use visibleViewController, I get that UISnapshotModalViewController. So using top instead of visible helps a lot. MEANWHILE, in the More nav controller, the list of items is really the UIMoreListController (which I also don't want to be rotated). That's always at the top of the more nav controller, so now I have this test:
if (vc == self.moreNavigationController || vc == [[self.moreNavigationController viewControllers] objectAtIndex:0]) {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
I'm still unable to have select VCs in the more controller allow rotation though. At least nothing broke in the other VCs though, and I don't need that force rotation step now. Still ... so close!
Maybe put my code into a separate helper class, and disable ARC on just that file (-fno-objc-arc), that'd be a simple approach.
Yup, that's exactly what I did, and it works! Meanwhile, I'm running into trouble with the code in this gist (I know, finally back on topic - ha!), but only when it comes to the More controller. Here's what I'm using now in order to avoid running into UIMoreListController and UISnapshotModalViewController:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// For non-nav controllers
UIViewController *vc = self.selectedViewController;
// For nav controllers
if ([vc isKindOfClass:[UINavigationController class]]) {
UINavigationController *navController = (UINavigationController *) vc;
vc = navController.topViewController;
}
// Exception for nav controllers under the more tab
// NOTE: With the More tab, I have yet to see vc be nil here. Hmm.
if (!vc) {
vc = self.moreNavigationController.topViewController;
}
// OPTIONAL: If this is the More navigation controller or the first view
// on its stack (UIMoreListController), keep it at portrait.
if (vc == self.moreNavigationController ||
[vc isKindOfClass:NSClassFromString(@"UIMoreListController")]) {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
return [vc shouldAutorotateToInterfaceOrientation:interfaceOrientation];
}
Its funny you should mention it, somewhere on my blog i wrote a post on how to find the active VC when the 'more' tab is selected. Its there...somewhere...
Anyway sounds like you've licked all your problems. Congrats.
Thanks! I really appreciate your patience and willingness to indulge me here in the comments.
As for that blog post, yep, that's how I arrived here. It's this one, right?
The thing that's got me confused now is that visibleController shouldn't work (at least certainly not on iOS 5, with those two private VCs that Apple exposes in there), but that's what you use in your code ... unless Apple changed something subtle as of iOS 5.
Thanks, and you're 100% correct - I want to lose that More tab so badly, you have no idea.
Ahh, yes, good call. Why am I still paranoid about using it? Perhaps if I sneak in respondsToSelector: I'll be content.
Hmph. Cast of 'int' to 'id' is disallowed with ARC. NSInvocation, here I come ... <!>
This is not nearly as elegant, but ARC is happy with it. If only I could make this a class extension with a method like xyz_setInterfaceOrientation: or something similar. (This code won't work though - [UIDevice methodSignatureForSelector:NSSelectorFromString(@"setOrientation:")] returns nil. Sigh.)