Posted at

UIWindowのwindowLevelについて あるいはソフトウェアキーボードより上にViewを表示する方法

More than 3 years have passed since last update.

ソフトウェアキーボードより上にViewを表示したい!と思って方法を調べました。


iOSのソフトウェアキーボードはUIWindow

iOSのソフトウェアキーボードの正体は、keyWindowとは別のUIWindowです。

ということで、独自にUIWindowを生成して表示すれば、キーボードより上にViewを表示することができます。

詳しくはこちら。

UIWindowのサブクラスをわざわざ作る必要はありません。

let currentWindow = UIApplication.sharedApplication().keyWindow

let newWindow = UIWindow(frame:currentWindow.frame)
newWindow.windowLevel = UIWindowLevelNormal
newWindow.addSubview(hogeView)
newWindow.makeKeyAndVisible()

こんな感じでnewWindowを表示できます。

windowを消したいときには、resignKeyWindow() を実行するのではなく、nilを入れる。


windowLevelの話

さて、UIWindow の表示順は、設定された windowLevel で制御されます。

UIWindowLevelはCGFloatのエイリアスです。

UIWindow.h に定数として定義されているのは以下の3種類。


  • UIWindowLevelNormal // = CGFloat(0)

  • UIWindowLevelAlert // = CGFloat(2000)

  • UIWindowLevelStatusBar // = CGFloat(1000)

ここで問題です。

キーボードより上にWindowを表示するためにはどのwindowLevelを設定すればいいでしょうか!?

答え。

全部ダメです。

マジかよ。


ためしにキーボードを表示した状態で、Windowの構成を見てみましょう。

検証環境はiOS9.1です。

検証コード:

UIApplication.sharedApplication().windows

結果:

[<UIWindow: 0x148214bb0; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x148218980>; layer = <UIWindowLayer: 0x148215ea0>>,

<UITextEffectsWindow: 0x146da70d0; frame = (0 0; 320 568); opaque = NO; autoresize = W+H; layer = <UIWindowLayer: 0x146da7450>>,
<UIRemoteKeyboardWindow: 0x149391ce0; frame = (0 0; 320 568); opaque = NO; autoresize = W+H; layer = <UIWindowLayer: 0x149392270>>
]

3つめのUIRemoteKeyboardWindowがソフトウェアキーボード。

iOS開発におけるウィンドウ「UIWindow」の知られざる活用方法とは? #iOS|CodeIQ MAGAZINE

によると、過去のバージョンのiOSではUITextEffectsWindowで表示されていたこともあったようですが…

なにはともあれ、これらのwindowLevelを確認してみると…

検証コード:

UIApplication.sharedApplication().windows.map { $0.windowLevel }

結果:

 [0.0, 1.0, 10000000.0]

10000000.0って何だよ…。


結論

独自のUIWindowをキーボードより上に表示したいなら、10000000.0より大きい数をwindowLevelに設定しましょう。

というか、 UIApplication.sharedApplication().windows をぶん回して、それより大きい数を設定しましょう。

newWindow.windowLevel = UIApplication.sharedApplication().windows.reduce(0, combine: { max($0, $1.windowLevel) }) + 1

みたいに書けばいいんじゃないかな。