最近在封装一个HUD弹窗,要获取最上层可以显示的window,所以研究了一下,发觉现在网上关于iOS11之后的变化,大家解释的都不是很清楚,我这里说一下我的处理方法,也解释一下原因。
之前大家获取最上层window的方法:
let window = UIApplication.shared.windows.last!
这个方法iOS11之后,经常会出现添加上去的视图不显示的问题,于是大家有的就用下面这个方法👇:
let window = UIApplication.shared.keyWindow!
可是你要知道,这个方法拿到的是keyWindow
,一般是我们在AppDelegate
里面我们创建的用于显示的window,很多情况下不是最上层的,比如有键盘正在输入的时候,键盘的window是在keyWindow上层的,所以你添加的hud弹窗等会被键盘挡住!
我们来看一下键盘显示的时候的window的层级关系:
- UIWindow 这个就是我们自己创建显示视图的keyWindow
- UITextEffectsWindow iOS8新增,键盘所在的window
-
UIRemoteKeyboardWindow iOS9新增,显示键盘视图的window
我们可以看到键盘显示的时候,iOS9之后UIApplication.shared.windows.last!
是我们显示键盘的UIRemoteKeyboardWindow
,这个window会在键盘隐藏之后就销毁,所以在键盘隐藏的瞬间,如果在上面添加视图,就会闪一下,或者不显示,就是因为这个window在键盘隐藏之后就移除销毁了
划重点,我来说一种我遇到超级坑的情况:iOS11之后,如果你使用UIImagePickerController
获取相册或者打开相机,系统也会自动创建一个UIRemoteKeyboardWindow
,虽然这个window的isHidden
等显示属性都是打开的,但是添加在上面的视图就是不显示,而且它一直霸占在windows的last位置还不释放,所以就出现了你取last添加视图,怎么也不显示的情况了
也就是说,当键盘显示的时候,last的UIRemoteKeyboardWindow
是可以添加我们的hud等视图,并显示出来,但是当键盘隐藏,last的UIRemoteKeyboardWindow
是不可以做显示的,我们看一下我获取最上层可显示window的方法:👇
func frontWindow() -> UIWindow? {
for window in UIApplication.shared.windows.reversed() {
guard window.screen == UIScreen.main else {
continue
}
guard !window.isHidden && window.alpha > 0 else {
continue
}
guard window.windowLevel >= .normal else {
continue
}
guard !window.description.hasPrefix("<UIRemoteKeyboardWindow") || IQKeyboardManager.shared.keyboardShowing else {
continue
}
return window
}
return nil
}
我比较喜欢使用guard
的语法糖,逆序遍历windows,前面的三个判断是排除了不作显示的window,然后最后一个判断就是如果是UIRemoteKeyboardWindow
,在键盘不显示的情况下,我们不使用它,继续挑选它下层的window
IQKeyboardManager
是我用来判断当前键盘状态的工具类(这个第三方很强大,两句代码就可以帮助你处理所有键盘遮挡输入框的情况)
我这里是对键盘的window做了单独判断,来确保它可以显示,如果你有其他的更方便的方法欢迎讨论
这样获取到的window就可以让我们的hud显示在包括键盘在内的最上层,不会被遮挡
欢迎更正错误和交流,回复评论和私信皆可 😊