前提条件として、以下の要件を満たすように作りました。
- iOS 3, 4, 5全てで正常に動作すること。iOS 3.0でも動作しなければならない。
- キーボードのframeを適切に取得できること
- キーボードが出てくるタイミング、消えるタイミング、キーボードの種類が変わるタイミング、全て取れること
■まずはログを見てみる
キーボードの動作のタイミング、およびキーボードのframeは、NSNotificationを使って取得することができます。使用するNotification名はUIWindowのドキュメントに以下のように定義されています。
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIWindow_Class/UIWindowClassReference/UIWindowClassReference.html
// Available from iOS 2 UIKeyboardWillShowNotification UIKeyboardDidShowNotification UIKeyboardWillHideNotification UIKeyboardDidHideNotification // Available from iOS 5 UIKeyboardWillChangeFrameNotification UIKeyboardDidChangeFrameNotificationで、これらのNotification名でNSNotificationCenterにobserverを追加すると、通知が飛んできます。飛んできたNSNotificationオブジェクトのuserInfoプロパティに特定のキーでキーボードのframeが格納されているというしくみです。使えるキーは以下のとおり。
// Available from iOS 3.2 UIKeyboardFrameBeginUserInfoKey UIKeyboardFrameEndUserInfoKey // Available from iOS 3.0 UIKeyboardAnimationCurveUserInfoKey UIKeyboardAnimationDurationUserInfoKey // Available from iOS 2.0 ~ 3.2 (Deprecated in newer versions) UIKeyboardCenterBeginUserInfoKey UIKeyboardCenterEndUserInfoKey UIKeyboardBoundsUserInfoKeyiOS 3, 4, 5すべてできちんと動作しなければならないので、これらをふまえて、以下のように実装します。
- Notification名にはUIKeyboardWillShowNotification, UIKeyboardDidShowNotification, UIKeyboardWillHideNotification, UIKeyboardDidHideNotificationを使う。
- userInfoのキーには、iOS 3.0/3.1のみUIKeyboardBoundsUserInfoKeyを使い、それ以外のバージョンではUIKeyboardFrameEndUserInfoKeyを使う。
■実装してみる
ということでまずはサンプルアプリを作って動かしてみて、実際に動作を見てみることにしました。大体こんな感じのコードです。
- (void)viewWillAppear:(BOOL)animated { // Notification observerを追加する [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onUIKeyboardWillShowNotification:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onUIKeyboardDidShowNotification:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onUIKeyboardWillHideNotification:) name:UIKeyboardWillHideNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onUIKeyboardDidHideNotification:) name:UIKeyboardDidHideNotification object:nil]; } - (void)viewWillDisappear:(BOOL)animated { // Notification observerを削除する [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)onUIKeyboardWillShowNotification:(NSNotification *)notification { NSLog(@"%s", __func__); NSLog(@" * userInfo = %@", notification.userInfo); // UIKeyboardFrameEndUserInfoKeyが使える時と使えない時で処理を分ける CGRect bounds; if (&UIKeyboardFrameEndUserInfoKey == NULL) { // iOS 3.0 or 3.1 // bounds bounds = [[notification.userInfo objectForKey:UIKeyboardBoundsUserInfoKey] CGRectValue]; } else { // それ以外 // frameだがoriginを使わないのでbounds扱いで良い bounds = [[notification.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; } // boundsを使った処理をここに書く }
ログはこんな感じに。
iOS 5.0, iPhone 4S
2011-10-19 10:35:15.007 SampleApp[5675:707] -[SampleViewController viewWillAppear:] // 前の画面の英字キーボードが一旦引っ込んで出てきている 2011-10-19 10:35:15.502 SampleApp[5675:707] -[SampleViewController onUIKeyboardDidHideNotification:] 2011-10-19 10:35:15.506 SampleApp[5675:707] * userInfo = { UIKeyboardAnimationCurveUserInfoKey = 0; UIKeyboardAnimationDurationUserInfoKey = "0.25"; UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}"; UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 372}"; UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 588}"; UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; UIKeyboardFrameChangedByUserInteraction = 0; UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 480}, {320, 216}}"; } 2011-10-19 10:35:15.509 SampleApp[5675:707] -[SampleViewController onUIKeyboardWillShowNotification:] 2011-10-19 10:35:15.510 SampleApp[5675:707] * userInfo = { UIKeyboardAnimationCurveUserInfoKey = 0; UIKeyboardAnimationDurationUserInfoKey = "0.35"; UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}"; UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 372}"; UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 372}"; UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; UIKeyboardFrameChangedByUserInteraction = 0; UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; } 2011-10-19 10:35:15.513 SampleApp[5675:707] -[SampleViewController onUIKeyboardDidShowNotification:] 2011-10-19 10:35:15.514 SampleApp[5675:707] * userInfo = { UIKeyboardAnimationCurveUserInfoKey = 0; UIKeyboardAnimationDurationUserInfoKey = "0.35"; UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}"; UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 372}"; UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 372}"; UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; UIKeyboardFrameChangedByUserInteraction = 0; UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; } 2011-10-19 10:35:16.162 SampleApp[5675:707] -[SampleViewController viewDidAppear:] // キーボード引っ込める 2011-10-19 10:35:44.246 SampleApp[5675:707] -[SampleViewController onUIKeyboardWillHideNotification:] 2011-10-19 10:35:44.247 SampleApp[5675:707] * userInfo = { UIKeyboardAnimationCurveUserInfoKey = 0; UIKeyboardAnimationDurationUserInfoKey = "0.25"; UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}"; UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 372}"; UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 588}"; UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; UIKeyboardFrameChangedByUserInteraction = 0; UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 480}, {320, 216}}"; } 2011-10-19 10:35:44.509 SampleApp[5675:707] -[SampleViewController onUIKeyboardDidHideNotification:] 2011-10-19 10:35:44.511 SampleApp[5675:707] * userInfo = { UIKeyboardAnimationCurveUserInfoKey = 0; UIKeyboardAnimationDurationUserInfoKey = "0.25"; UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}"; UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 372}"; UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 588}"; UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; UIKeyboardFrameChangedByUserInteraction = 0; UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 480}, {320, 216}}"; } // キーボード出す 2011-10-19 10:35:55.135 SampleApp[5675:707] -[SampleViewController onUIKeyboardWillShowNotification:] 2011-10-19 10:35:55.136 SampleApp[5675:707] * userInfo = { UIKeyboardAnimationCurveUserInfoKey = 0; UIKeyboardAnimationDurationUserInfoKey = "0.25"; UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}"; UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 588}"; UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 372}"; UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 480}, {320, 216}}"; UIKeyboardFrameChangedByUserInteraction = 0; UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; } 2011-10-19 10:35:55.397 SampleApp[5675:707] -[SampleViewController onUIKeyboardDidShowNotification:] 2011-10-19 10:35:55.398 SampleApp[5675:707] * userInfo = { UIKeyboardAnimationCurveUserInfoKey = 0; UIKeyboardAnimationDurationUserInfoKey = "0.25"; UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}"; UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 588}"; UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 372}"; UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 480}, {320, 216}}"; UIKeyboardFrameChangedByUserInteraction = 0; UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; } // 日本語キーボードに変更 2011-10-19 10:35:58.167 SampleApp[5675:707] -[SampleViewController onUIKeyboardWillShowNotification:] 2011-10-19 10:35:58.168 SampleApp[5675:707] * userInfo = { UIKeyboardAnimationCurveUserInfoKey = 0; UIKeyboardAnimationDurationUserInfoKey = 0; UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 252}}"; UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 390}"; UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 354}"; UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; UIKeyboardFrameChangedByUserInteraction = 0; UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 228}, {320, 252}}"; } 2011-10-19 10:35:58.170 SampleApp[5675:707] -[SampleViewController onUIKeyboardDidShowNotification:] 2011-10-19 10:35:58.171 SampleApp[5675:707] * userInfo = { UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 252}}"; UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 390}"; UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 354}"; UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; UIKeyboardFrameChangedByUserInteraction = 0; UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 228}, {320, 252}}"; } // 英語キーボードに変更 2011-10-19 10:36:00.483 SampleApp[5675:707] -[SampleViewController onUIKeyboardWillShowNotification:] 2011-10-19 10:36:00.484 SampleApp[5675:707] * userInfo = { UIKeyboardAnimationCurveUserInfoKey = 0; UIKeyboardAnimationDurationUserInfoKey = 0; UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}"; UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 336}"; UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 372}"; UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 228}, {320, 252}}"; UIKeyboardFrameChangedByUserInteraction = 0; UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; } 2011-10-19 10:36:00.485 SampleApp[5675:707] -[SampleViewController onUIKeyboardDidShowNotification:] 2011-10-19 10:36:00.486 SampleApp[5675:707] * userInfo = { UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {320, 216}}"; UIKeyboardCenterBeginUserInfoKey = "NSPoint: {160, 336}"; UIKeyboardCenterEndUserInfoKey = "NSPoint: {160, 372}"; UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 228}, {320, 252}}"; UIKeyboardFrameChangedByUserInteraction = 0; UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 264}, {320, 216}}"; } // 画面を抜ける、キーボードが隠れる通知より先にviewWillDissappearされるので通知が来ない 2011-10-19 10:36:03.570 SampleApp[5675:707] -[SampleViewController viewWillDisappear:] 2011-10-19 10:36:03.986 SampleApp[5675:707] -[SampleViewController viewDidDisappear:]iOS 3.1.3, iPhone 3G
2011-10-19 10:43:46.548 SampleApp[352:207] -[SampleViewController viewWillAppear:] // 前の画面の英字キーボードが一旦引っ込んで出てきているのだが、iOS 3.1.3では引っ込む側の挙動が見られない。出てくるだけになっているようだ。 // さらにDidShowの通知がキーボードがviewDidAppearの呼び出しのあとに行われるようになっている。 // どうやらキーボードがどこに属しているのかが違うみたいだな。 2011-10-19 10:43:47.202 SampleApp[352:207] -[SampleViewController onUIKeyboardWillShowNotification:] 2011-10-19 10:43:47.210 SampleApp[352:207] * userInfo = { UIKeyboardAnimationCurveUserInfoKey = 0; UIKeyboardAnimationDurationUserInfoKey = 0.35; UIKeyboardBoundsUserInfoKey = NSRect: {{0, 0}, {320, 216}}; UIKeyboardCenterBeginUserInfoKey = NSPoint: {480, 372}; UIKeyboardCenterEndUserInfoKey = NSPoint: {160, 372}; } 2011-10-19 10:43:48.253 SampleApp[352:207] -[SampleViewController viewDidAppear:] 2011-10-19 10:43:48.315 SampleApp[352:207] -[SampleViewController onUIKeyboardDidShowNotification:] 2011-10-19 10:43:48.336 SampleApp[352:207] * userInfo = { UIKeyboardBoundsUserInfoKey = NSRect: {{0, 0}, {320, 216}}; UIKeyboardCenterBeginUserInfoKey = NSPoint: {160, 588}; UIKeyboardCenterEndUserInfoKey = NSPoint: {160, 372}; } // キーボード隠す 2011-10-19 10:43:52.854 SampleApp[352:207] -[SampleViewController onUIKeyboardWillHideNotification:] 2011-10-19 10:43:52.862 SampleApp[352:207] * userInfo = { UIKeyboardAnimationCurveUserInfoKey = 0; UIKeyboardAnimationDurationUserInfoKey = 0.300000011920929; UIKeyboardBoundsUserInfoKey = NSRect: {{0, 0}, {320, 216}}; UIKeyboardCenterBeginUserInfoKey = NSPoint: {160, 372}; UIKeyboardCenterEndUserInfoKey = NSPoint: {160, 588}; } 2011-10-19 10:43:53.183 SampleApp[352:207] -[SampleViewController onUIKeyboardDidHideNotification:] 2011-10-19 10:43:53.188 SampleApp[352:207] * userInfo = { UIKeyboardBoundsUserInfoKey = NSRect: {{0, 0}, {320, 216}}; UIKeyboardCenterBeginUserInfoKey = NSPoint: {160, 372}; UIKeyboardCenterEndUserInfoKey = NSPoint: {160, 588}; } // キーボード出す 2011-10-19 10:43:54.621 SampleApp[352:207] -[SampleViewController onUIKeyboardWillShowNotification:] 2011-10-19 10:43:54.629 SampleApp[352:207] * userInfo = { UIKeyboardAnimationCurveUserInfoKey = 0; UIKeyboardAnimationDurationUserInfoKey = 0.300000011920929; UIKeyboardBoundsUserInfoKey = NSRect: {{0, 0}, {320, 216}}; UIKeyboardCenterBeginUserInfoKey = NSPoint: {160, 588}; UIKeyboardCenterEndUserInfoKey = NSPoint: {160, 372}; } 2011-10-19 10:43:54.972 SampleApp[352:207] -[SampleViewController onUIKeyboardDidShowNotification:] 2011-10-19 10:43:54.977 SampleApp[352:207] * userInfo = { UIKeyboardBoundsUserInfoKey = NSRect: {{0, 0}, {320, 216}}; UIKeyboardCenterBeginUserInfoKey = NSPoint: {160, 588}; UIKeyboardCenterEndUserInfoKey = NSPoint: {160, 372}; } // キーボードの種類をここで英語→日本語、日本語→英語に変えているのだが、通知が来ない // だがiOS 3ではキーボードの種類によって高さが違うということが(基本)ないので気にしなくて良い [Switching to process 11779 thread 0x2e03] warning: No copy of問題なさそうですね。キーボードの種類が切り替わったタイミングにもUIKeyboardWillShowNotificationとUIKeyboardWillHideNotificationがきちんと呼ばれているようです。これならUIKeyboardWillChangeFrameNotificationを使う必要はあんまり無いように思えます。実際、UIKeyboardWillChangeFrameNotificationを使わないでUIKeyboardWillShowNotificationとUIKeyboardWillHideNotificationだけを使ったアプリをリリースしていますが、特に問題なさそうです。found locally, reading from memory on remote device. This may slow down the debug session. // 画面抜ける、viewWillDisappearより先にKeyboardが隠れる通知が来る 2011-10-19 10:44:07.442 SampleApp[352:207] -[SampleViewController onUIKeyboardWillHideNotification:] 2011-10-19 10:44:07.454 SampleApp[352:207] * userInfo = { UIKeyboardAnimationCurveUserInfoKey = 0; UIKeyboardAnimationDurationUserInfoKey = 0.35; UIKeyboardBoundsUserInfoKey = NSRect: {{0, 0}, {320, 216}}; UIKeyboardCenterBeginUserInfoKey = NSPoint: {160, 372}; UIKeyboardCenterEndUserInfoKey = NSPoint: {480, 372}; } 2011-10-19 10:44:07.472 SampleApp[352:207] -[SampleViewController viewWillDisappear:] 2011-10-19 10:44:08.374 SampleApp[352:207] -[SampleViewController viewDidDisappear:]