My rootViewController is a UITabbarController and contains UINavigationControllers.
In order to restrict rotation, since iOS6, on some but not all views, I have subclassed both of the these controllers to respect the shouldAutorotate response of their topmost or visible view.
This all works just perfectly... most of the time.
In one case, starting from a view (UITableviewController) that does not allow rotation.
I push a view that does allow autorotate, rotate to landscape then back to portrait.
Then when I pop the view, all appears well.
But now if I rotate the device, the status bar alone rotates, leaving a blank space at the top and covering a part of the left side of the current view. The current view does not rotate (as it should not). Only the status bar rotates, back and forth. The view remains responsive, receives touches and works as normal, doesn't resize or respond to rotation in any way (as it should not).
This behavior continues until I kill and restart the app. Once it happens I have found no other way to make it stop. On restart all works properly again and will work properly for an indeterminate duration. I can sit there pushing, popping, rotating back and forth to my hearts content with no issues.
I have never been able to recreate this in simulator.
It happens for no apparent reason, I cannot make it happen. I'd say 85% of the time it works properly. I thought I had it fixed many times when it did not occur for days or even weeks. Then, out of the blue it happens again.
I cannot find a post in any forums which describe this behavior, so I am not finding even a place to start to debug. How could the status bar swing from top to side and the view does not resize or rotate or respond in any way. If I knew how to make that happen, I would have a place to start.
I experienced the same problem when adding a subclassed UIWindow (in my case, a status bar overlay).
The solution was to set the rootViewController of this new UIWindow to a subclassed UIViewController that had -(BOOL)shouldAutorotate implemented. This prevented the status bar from rotating when it wasn't supposed to.
Related
I have a controller that manages a UITextView. Normally, in an app that supports multiple interface orientations, I would simply override -shouldAutorotateToInterfaceOrientation:, set the appropriate auto-resizing masks, and go along my merry way. While going along my merry way, if I were to begin editing this UITextField in, say, portrait mode, then rotate the device to a landscape orientation, the keyboard would animate nicely to landscape mode along with the rest of the view. This is what I want.
Now, in this particular app, I have a root view that must only ever be in portrait mode. This is a camera preview view. I also have an overlay-view for the camera-view that I would like to have support all interface orientations. This is where the UITextView is located.
So, as a result of the fixed-orientation root view, I cannot use built in rotation: I have to do this manually. This isn't a problem, I can register for the UIDeviceOrientationDidChangeNotification and manually animate the appropriate rotation and frame adjustment for the overlay view.
That's good enough to get the view positioned correctly, but the keyboard would still only ever show in portrait orientation. Again, I can correct this with UIApplication's -setStatusBarOrientation: every time I get a notification that the device has rotated.
But that's where the problem arises. I would hope that the combination of:
Register for rotation notifications
Manually rotate view
Set status bar orientation (with animation)
would be enough to exactly replicate the automatic rotation behavior. But it falls short in one important way: If the keyboard were to be shown in one orientation, then rotated to another, the keyboard would remain defiantly in the original position, as if -setStatusBarOrientation: were never called. As far as I know, the only thing that affects the keyboard's presentation is the application's status bar orientation, and there is only the one method for setting it. So am I out of luck? Is it really impossible to make the keyboard rotate with a view manually like it does with auto-rotation?
Addendum
In an effort to avoid covering old ground, I've tried the following solutions hacks:
Upon orientation change, resign and immediately become first responder again. This does make the keyboard move to the appropriate orientation, but it does so without animation, and occasionally does really bad things like show a landscape-sized keyboard in portrait and vice-versa.
Upon orientation change just resign first responder. This also has the odd behavior of instantly moving (but not resizing) the keyboard to the new orientation, then dismissing it with animation. It's very ugly and jarring.
One of our apps does this (because we didn't say "no!" loudly enough when presented with the original design) using crazy "orientation stack" code. I remember fixing this problem by disabling orientation when the keyboard is up (ickyyyy).
Instead, consider supporting rotation by "counter-rotating" the camera preview view instead of forcing the VC to portrait-only. If this looks a bit odd, you can do the "nasty hack" version: Add the camera preview view as a subview of the window, under your VC's view, and give your VC's view a transparent background.
Maybe late to the party. I stumbled upon the same problem. Trying to fix it in iOS 6.
Apparently Apple didn't recognize this problem in the meanwhile, otherwise they would've fixed it by now: have the -setStatusBarOrientation: method include other interface elements besides only the status bar as well, or have alternative methods to set the orientation of the keyboard and other elements. A pity they didn't
Disabling orientation rotation when the keyboard is up, is no solution for me, but I found another simple solution hack:
return NO in -(BOOL)shouldAutorotate
return UIInterfaceOrientationMaskAll in -(NSUInteger)supportedInterfaceOrientations
listen for device rotation notifications
Then when you receive such notification:
hide the status bar
when the keyboard is up, dismiss the keyboard ([myTextField resignFirstResponder])
perform manual rotation animation
set the orientation of the statusbar ([sharedApplication setStatusBarOrientation:newOrientation animated:NO])
show the status bar
if the keyboard was visible before, present it again ([myTextField becomeFirstResponder])
The effect is that the status bar and the keyboard gently slide out before the manual rotation animation and gracefully slide back in, when the animation is done.
BTW, I also found this interesting article by Corey Floyd: http://coreyfloyd.tumblr.com/post/8212203583/2-ways-to-rotate
Although his second method: Tricking UIKit, only caused more confusion with me.
Once again, I'm almost entirely sure this is something dumb that I'm doing, but I've been banging my head against this one for hours & am getting nowhere.
I'm trying to restructure the view hierarchy of my app. I need to be able to detect user interface orientation changes globally in order to correctly rotate a "Loading" view displayed when the app is downloading content. (device orientation changes seem to fire at different times, causing the view that needs to respond to these events to rotate sporadically).
The app previously added a UINavigationController's view to the main window. I modified the hierarchy to add the view of a UIViewController subclass to the main window, and added the view of the UINavigationController to the subclass's view. The UIViewController subclass manages the display & rotation of the "Loading" subview, and I was expecting the rest of the app to continue behaving normally, as inserting one extra empty view into the hierarchy didn't feel like I was changing too much.
My initial problem was the positioning of the UINavigationController - it was 20 pixels too low, resulting in a gap between the status bar and the navigation bar, and cutting off the bottom 20 pixels of the tab bar. I was able to adjust this by setting the frame property of the UINavigationController's view to the bounds property of the UIViewController's view, which corrected the position.
However, now I'm stuck with a 20-pixel-high dark "overlay" on top of my navigation bar. If I were to guess, I'd say it was black with 50% opacity. Touch events on this bar don't work (e.g. if I try to tap the "Back" button through the overlay, nothing happens). The fact that the height is equal to that of the status bar hasn't escaped me, but I'm at a total loss as to what could be causing it.
I hate feeling this stupid, so if anyone has any insight into this problem, you'd really make my day. Thanks in advance!
OK, a few things pop out from your post.
My initial problem was the positioning of the
UINavigationController - it was 20
pixels too low
This makes me believe it is related to your new problem.
I was able to adjust this by setting
the frame property of the
UINavigationController's view to the
bounds property of the
UIViewController's view
This sounds like the view it was loaded onto was offset 20 pixels, and when you set it to the bounds, it repositioned it on the windows view space.
Touch events on this bar don't work
(e.g. if I try to tap the "Back"
button through the overlay, nothing
happens)
This is the big thing. If touch events aren't being sent to the view, then what that means is that the OS doesn't see a view where you are pressing (or rather the view you want it to), so that view doesn't get the message to do something.
From what you have said, I believe your problem is with your base view controller that you just added. Try redoing the frame on, making it conform to where you want. Then take out the code you put in to set the navigation controllers frame. The navigation controller should fit to the view you added too, and once you have that main view where it needs to be (20 pixels higher apparently), then everything should work.
I am coding against iOS 3.0 and I am trying to add rotation (landscape) support to my app.
Everything about the rotation was easy enough. However, after rotation to landscape mode from portrait, the right-third of the screen (480-320) is unresponsive to all events (touches, drag, etc.). I've verified that the key window is receiving the events but those events are not being passed to the buttons and UIViews on the right (in landscape) side of the screen.
I am using NSNotificationCenter to receive orientation changed events (and not using autorotate flag).
I did see: link text but that wasn't very helpful to my case.
Stuck. Need help. Thanks.
I've fixed the problem I was having .. I basically needed to do [self.navigationController.view setNeedsLayout].
The way I understand this (which maybe incorrect is that self.navigationController.view.frame was same as self.view.frame and both were equal to (x=0,y=0,width=320,height=480). I then rotated self.view by M_PI/2 and did a number of frame manipulation on select self.view.subviews to get everything to animate/position/scale correctly.
That worked out okay but the navigation controller was not willing to acknowledge touch events to parts of self.view there were to the right of 320. In essence, if this self.navigationController.view.clipsToBounds were true, it might not even have shown that part of self.view.
Anyway, setting setNeedsLayout on the navigation controller's view resolved the issue. I hope this helps someone else.
I have this UINavigationController-based iPad app that supports all 4 orientations. When I rotate the device, I'd like the background image to stay still and only have the UI on top of it get rearranged/rotated.
I've succeeeded by returning NO to the UIViewController shouldAutorotateToInterfaceOrientation: and handle the orientation myself using UIDeviceOrientationDidChangeNotification
Since only the current view gets rotated (and not the whole window), the UINavigationController push animation doesn't get adjusted. So when you're holding the iPad in landscape right, it pushes the view from the bottom. When you're holding it upside-down, it pushes the view from the left (it should always be from the right).
Any better way to have that background image remain steady while rotating everything else? Or fixing the UINavigationController push animation? I'd like to stick with the controllers if possible (ie not having to play around with manually animating view frames, etc).
As you have seen, UINavigationControllers are very particular about orientation.
I think you are probably better off rotating the background every time the interface is rotated.
This is just an example of the basic problem I'm having, so don't worry if this situation sounds a bit pointless ;)
Let's say I have an app that's mainly a UINavigationController just two levels deep. The top level is a table with a list of image filenames, and the second level has just a UIImageView showing the image for the filename you tapped.
For an app such as this, does anyone know a good way to allow the table at the top level to autorotate while keeping the second level of images fixed in portrait mode?
So far I've been able to almost get there... but when I tap a filename while in landscape mode, the image slides into view in the wrong orientation even if the second level view controller's shouldAutorotateToInterfaceOrientation returns yes for only portrait modes.
There was no good way to do this in iPhone OS 2.x, but in 3.0, they've dramatically improved it.
In 2.x, the shouldAutorotateToInterfaceOrientation: delegate method was only obeyed for changes to the orientation, so you'd get the behavior you describe: if it was rotated in another view controller, it would stay rotated through pushes and pops even if the new view controller didn't support rotation to that orientation.
In 3.0, UINavigationController polls shouldAutorotateToInterfaceOrientation: on each push or pop and obeys what it returns the way you'd expect, e.g.: if you're currently rotated in Landscape Left orientation, and you push an instance of a view controller that only supports Portrait orientation via shouldAutorotateToInterfaceOrientation:, it automatically and instantly flips the logical orientation and slides in the new view the correct way in Portrait orientation.
Note that this will only work on applications linked against (and therefore requiring) 3.0. For applications linked against 2.x, it will emulate the old behavior.
The problem is that if you use auto rotation the entire UI (including the UIWindow instance I believe) is rotated.
Anything pushed onto the navigation controller at this point will be done in landscape.
So when you push the imageview, that is exactly what you get.
To get this to work, you have to either:
Handle the rotation of the root view
manually (using a transform)
Unrotate the image view by -PI/2
using a transform.
Either way you have to perform the transforms manually to get this to work.
As a side note, this may be bad UI design. As a user, I would expect as I drill down for images to appear rightside up. But this is without knowing the exact context of your app.