この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
UIWindowとは、特別なUIViewでありビュー階層のルートとなるものです。
通常のアプリ作成では、実装の対象は、このウィンドウに載せられたUIViewControllerが主であり、ウインドウ自体は、テンプレート任せとなっているのであまり意識されていないと思います。
テンプレートから生成されるコードでは、AppDelegateクラスに、次のような1つのUIWindowを保持するプロパティが設定されています。
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@end
しかし、このUIWindowは、アプリに1つではありません。 下記のコードで、アプリのウインドウを全て列挙することができます。上のwindowプロパティは、このうちの1つだったという事です。
NSArray<UIWindow*> *windows = [UIApplication sharedApplication].windows;
今回は、この複数のUIWindowのについて確かめて見ました。 なおこの情報は、iOSのバージョンによって違いますので、本記事はiOS9.3での結果である事を予めご了承ください。
2 試験準備
まずは、UIApplicationが持つUIWindowの数と、それぞれの名前やプロパティを出力するメソッドをAppDelegate.mに作成します。
@implementation AppDelegate
// ログ出力
- (void)log:(NSString *)title{
NSLog(@"[%@]",title);
NSArray<UIWindow*> *windows = [UIApplication sharedApplication].windows;
NSLog(@"Application.windows.count = %lu",(unsigned long)windows.count);
for( UIWindow *window in windows){
NSLog(@"%@ keywindow = %d hidden=%d lebel=%f",window.class.description,window.keyWindow, window.hidden,window.windowLevel);
}
}
上記は、次のような出力を得られます。
[TITLE] <= ログ用のタグ
Application.windows.count = 2 <= 現在windowsに2つのUIWindowがある
UIWindow keywindow = 1 hidden=0 lebel=0.000000 <= 1つ目は、UIWindow レベルは0
UITextEffectsWindow keywindow = 0 hidden=0 lebel=10.000000 <= 2つ目のUITextEffectsWindow レベルは10
そして、UIWindowが表示された時と、非表示に成った時のNotificationでこれをコールするようにします。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowDidBecomeVisible:)
name:UIWindowDidBecomeVisibleNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowDidBecomeHidden:)
name:UIWindowDidBecomeHiddenNotification
object:nil];
return YES;
}
// UIWindowが表示された時
-(void)windowDidBecomeVisible:(NSNotification*)notification
{
[self log:@"Visible"];// ログ出力
}
// UIWindowが非表示された時
-(void)windowDidBecomeHidden:(NSNotification*)notification
{
[self log:@"Hidden"];// ログ出力
}
続いて操作画面です。
各UIは、それぞれ次の動作を行います。
①アラートを表示
②TextFieldを置いただけ
③キーボードを非表示にするコード
④ログ出力
// ③ hideKeyboard
- (IBAction)tapHideKeyboard:(id)sender {
[self.view endEditing:YES];//キーボード非表示
}
// ④ Comfirmのボタンを押すと、ログを出力する
- (IBAction)tapComfirmButton:(id)sender {
AppDelegate *application = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[application log:@"Confirm"];
}
3 動作確認
(1) 起動時
アプリを起動した時点で、UIWindowが一つ表示されます。レベルは0です。
[Visible]
Application.windows.count = 1
UIWindow keywindow = 0 hidden=0 lebel=0.000000
その後、すぐに確認すると、表示時には、keywindow=FALSEだったのが、すぐにTRUEに変化しています。
[Confirm]
Application.windows.count = 1
UIWindow keywindow = 1 hidden=0 lebel=0.000000
(2) アラート表示
アラートを表示すると、UITextEffectsWindowというウインドウが追加されました。レベルが10なので、これが最上位になっているようです。
[Visible]
Application.windows.count = 2
UIWindow keywindow = 1 hidden=0 lebel=0.000000
UITextEffectsWindow keywindow = 0 hidden=0 lebel=10.000000
(3) アラートをクローズ
アラートをクローズしても、UIWindowsのNotificationは発生しません。(表示・非表示の変化はない)。 これは、Confirmボタンで確認しても変化がないことが確認できました。
[Confirm]
Application.windows.count = 2
UIWindow keywindow = 1 hidden=0 lebel=0.000000
UITextEffectsWindow keywindow = 0 hidden=0 lebel=10.000000
(4) TextFieldにフォーカスを与える(キーボード表示)
キーボードが表示された時、新たにUIRemoteKeyboardWindowというウインドウが追加されました。レベルが10000000なので、今度は、これが最上位になっているのでのしょう。
[Visible]
Application.windows.count = 3
UIWindow keywindow = 1 hidden=0 lebel=0.000000
UITextEffectsWindow keywindow = 0 hidden=0 lebel=1.000000
UIRemoteKeyboardWindow keywindow = 0 hidden=0 lebel=10000000.000000
(5) キーボードを非表示にする
キーボードを非表示にすると、まず、UIRemoteKeyboardWindowのhidden属性がFALSEになり、その後、消えました。この時点では、レベルが一番高いUITextEffectsWindowが最上位になっていると考えられます。
[Hidden]
Application.windows.count = 3
UIWindow keywindow = 1 hidden=0 lebel=0.000000
UITextEffectsWindow keywindow = 0 hidden=0 lebel=1.000000
UIRemoteKeyboardWindow keywindow = 0 hidden=1 lebel=10000000.000000
[Confirm]
Application.windows.count = 2
UIWindow keywindow = 1 hidden=0 lebel=0.000000
UITextEffectsWindow keywindow = 0 hidden=0 lebel=1.000000
(6) キーボードの表示状態からアラートを出す
キーボードが表示されている時の状態は、下記の通りですが、
[Visible]
Application.windows.count = 3
UIWindow keywindow = 1 hidden=0 lebel=0.000000
UITextEffectsWindow keywindow = 0 hidden=0 lebel=1.000000
UIRemoteKeyboardWindow keywindow = 0 hidden=0 lebel=10000000.000000
この状態から、アラート表示すると、
UIRemoteKeyboardWindowのhiddenがTRUEに設定され、見えなくなります。 上の図を見ても、キーボードが消えているのを確認できます。
[Hidden]
Application.windows.count = 3
UIWindow keywindow = 1 hidden=0 lebel=0.000000
UITextEffectsWindow keywindow = 0 hidden=0 lebel=1.000000
UIRemoteKeyboardWindow keywindow = 0 hidden=1 lebel=10000000.000000
そして、アラートを閉じると、再びキーボードが出現して、UIRemoteKeyboardWindowのhiddenがFALSEに戻っています。
[Visible]
Application.windows.count = 3
UIWindow keywindow = 1 hidden=0 lebel=0.000000
UITextEffectsWindow keywindow = 0 hidden=0 lebel=1.000000
UIRemoteKeyboardWindow keywindow = 0 hidden=0 lebel=10000000.000000
4 最後に
最初に、書いた通り、この情報はiOSのバージョンによって変化します。試しに、先の動作試験をiOS8.2で実行すると、キーボード表示時に次のような結果となります。
[Confirm]
Application.windows.count = 3
UIWindow keywindow = 1 hidden=0 lebel=0.000000
UITextEffectsWindow keywindow = 0 hidden=0 lebel=1.000000
UITextEffectsWindow keywindow = 0 hidden=0 lebel=2100.000000
レベルの数値やクラス名などは、iOSによって違うため、そのまま決めうちで使用する事は出来ないことが分かります。
また、予想外だったのは、アラートを一度でも表示すると、追加されたUITextEffectsWindowが、最後まで残り続けることでした。
この結果を踏まえて次は、UIWindowsを自前で追加して、更に詳しく動作を追ってみたいと思います。
参考資料
UIKit Framework Reference UIWindow Class Reference
iOS開発におけるウィンドウ「UIWindow」の知られざる活用方法とは? #iOS
makeKeyWindow vs makeKeyAndVisible