(Updated! See bottom.)
There’s a lot to dislike in Apple’s rotation APIs.
Let’s start with how they changed, from iOS 5 to iOS 6, with no buffer period.
Normally, when a new API is introduced, the old one is deprecated but still works the same way for a few more major versions. Here, a new version was introduced, and the old version stopped working immediately. (What was the rush?)
Then, there’s how the keyboard is handled. If you have the keyboard visible on your iOS device, and you rotate the device from portrait to landscape or vice versa, you’ll see the keyboard change its width (and change its height slightly), but otherwise remain visible throughout. So you’d think that the underlying APIs would reflect that.
Instead, as far as the official APIs are concerned, your application’s code is notified that the keyboard is hidden, then that the view is rotated, then that the keyboard is shown again.
This totally screws you over if you have content near the bottom of your view, that you want to animate smoothly along with the (always visible) keyboard.
I have a GitHub project, (also) called Keyboard-Schmeeboard (because it’s a good name), which demonstrates this problem. You’ll have to comment out the
#define kIOS5Workaround and
#define kIOS6Workaround lines in ViewController.m to see the broken behavior on iOS 5 and iOS 6.
As the above line suggests, however, there are workarounds. On iOS 5, you can override
shouldAutorotateToInterfaceOrientation: to tell yourself a rotation is taking place before you’re told the keyboard is being hidden. Then, you simply ignore the keyboard-will-hide notification entirely, and use the keyboard-will-show notification to animate your view to the correct new location.
Note, you can’t do it in the rotation call where you would normally do it if the keyboard weren’t visible, because you don’t know the new height of the keyboard yet. You could hardcode a keyboard height in the rotation call, but that ignores localized keyboards of variable height, so it’s a bad idea.
The animation information you’re given in the keyboard-will-show method doesn’t exactly match the actual rotation animation, but it’s all you’re going to get, and it’s close enough. In Keyboard Schmeeboard, to see the mismatch, uncomment the
#define kIOS5Workaround line again in ViewController.m, and when you rotate the app with the keyboard present, look for the telltale slivers of pink color as the animation proceeds, due to the background view showing through. In a real-world project, I’d make sure the background view had a color matching the content view, so that such minor differences were unnoticeable.
(It occurs to me as I’m writing this that I could save off the information from the rotation call, and use it during the keyboard-will-show notification. Too fiddly? Worth exploring, anyway.)
In iOS 6, there’s a similar call you can override,
supportedInterfaceOrientations. But here’s the rub. Unlike the iOS 5 workaround, this iOS 6 call is also called if you just dip your device back and then up again, without rotating it either to the right or the left. If you go ahead and try this on an iOS 6 device (being sure to uncomment the
#define kIOS6Workaround line again in ViewController.m), and then hide the keyboard, the space where the keyboard was will remain pink. Why? Because your “is rotating with keyboard” flag was set in
supportedInterfaceOrientations and never turned back off. (Because you weren’t actually rotating.)
I’m actually looking for a solution to this problem right now, so if this rings any bells for people, try out the GitHub project, and let me know your thoughts in the comments or via Twitter.
Joel Bernstein tweets:
@apontious You may be able to disable both hacks and use UIViewAnimationOptionBeginFromCurrentState. Tried it, seems to work.
This does indeed work.
What he means is, dispense with trying to identify when you’re rotating or not. Instead, just implement the keyboard-will-show and keyboard-will-hide animations without any extra logic. But, for keyboard-will-show, specify UIViewAnimationOptionBeginFromCurrentState as one of the animation options.
This will override the changes that keyboard-will-hide was attempting to make, and just animate properly from the previous keyboard location to the next keyboard location. Neat.
I’ve updated the GitHub project to have a second project in it, Corrected Keyboard Schmeeboard, with this fix.