日常开发中,我们经常写这样的代码:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.backgroundColor = UIColor.white
window?.makeKeyAndVisible()
if #available(iOS 9.0, *) {
window?.rootViewController = UIViewController()
}
// todo ...
let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateInitialViewController()
window?.rootViewController = vc
return true
}
UIWindow 是应用程序显示的背景,以及将接受的事件传递到视图的对象。
当我们打iPhone上的应用程序切换器,就会看到当前正在运行的应用程序的窗口。
大多数应用程序仅需要提供一个主窗口,即可在屏幕上显示应用内容,也可以根据需要创建其它窗口显示其它内容。
默认情况下,Xcode会提供应用程序的主窗口,即 Storyboards 会自动给 AppDelegate 的 window 属性提供值;如果不使用 Storyboards 则需要我们自己创建 window。
UIWindow 是一个特殊的 UIView,iOS 程序启动完毕后,创建的第一个视图控件就是 UIWindow,接着创建控制器的view,最后将控制器的View添加到 UIWindow 上,于是控制器的 View 就显示在屏幕上了。
// 是否为 keyWindow
open var isKeyWindow: Bool { get }
// 系统自动调用该方法,子类可以重写该方法来执行与成为主窗口相关的任务
open func becomeKey()
// 系统自动调用以通知窗口它不再是键窗口
open func resignKey()
// 使对象成为主窗口,不更改window是否可见
open func makeKey()
// 显示窗口,并使其成为keyWindow
open func makeKeyAndVisible()
创建 window 时,它的 hidden 值默认为 YES,因此不会显示。通过调用makeKeyAndVisible可以让 window 显示出来,或者直接设置 window?.isHidden = false 。
keywindow 用来接收键盘输入以及非触摸类的消息事件,程序中每个时刻只能有一个 UIWindow 是 keyWindow。如果某个 UIWindow 内部的文本框不能输入文字,可能是因为这个 UIWindow 不是 keyWindow。
在进行 keyWindow 操作时,系统会发送如下通知:
UIWindow.didBecomeVisibleNotification
UIWindow.didBecomeHiddenNotification
UIWindow.didBecomeKeyNotification
UIWindow.didResignKeyNotification
设置了 window 的 rootViewController 后,会自动的把 View 添加到 window 中,并且负责管理 rootViewController 的生命周期。VC 会跟随 Window 的大小更改而更改
var windowLevel: UIWindow.Level
windowLevel 的值由 CGFloat 初始化获得,决定窗口在Z轴上显示的位置,默认值为 UIWindow.Level.normal(值为0),值越大越靠前显示
UIWindow.Level.normal. ==> 0.0
UIWindow.Level.alert ==> 2000.0
UIWindow.Level.statusBar ==> 1000.0
func sendEvent(_ event: UIEvent)
UIApplication 调用该方法给 window 分发事件,window 再将事件分发到合适的目标,比如:可以调用此方法将自定义事件分派到窗口的事件响应程序链。
// 把该window中的一个坐标转换成在目标window中的坐标值
open func convert(_ point: CGPoint, to window: UIWindow?) -> CGPoint
// 把目标window中的一个坐标转换成在该window中的坐标值
open func convert(_ point: CGPoint, from window: UIWindow?) -> CGPoint
// 把该window 中的一个frame转换成在目标window中的frame
open func convert(_ rect: CGRect, to window: UIWindow?) -> CGRect
// 把目标window中的一个frame转换成在该window中的frame
open func convert(_ rect: CGRect, from window: UIWindow?) -> CGRect
1、UITextEffectsWindow,键盘所在的 window,iOS 8中新增的,其 windowLevel = 10;
2、UIRemoteKeyboardWindow,显示键盘按钮,iOS 9中新增的;
3、手机状态栏的UIStatusBarWindow, 属于系统级别, 不被App所持有;
4、UIAlertView 显示 _UIAlertControllerShimPresenterWindow;
方式一:通过UIView
有时候window为null,原因是view没有完全加载完成或者在ViewDidload中才刚刚加载完,view的window属性无法被获取
self.view.window
方式二:通过UIApplication
// 获取keyWindow,但不一定是显示在最前面的
let window = UIApplication.shared.keyWindow
// 获取顶层window
let window2 = UIApplication.shared.windows.last
1、创建一个新的 window 显示 Alert
// 该window是全局变量
var window = UIWindow(frame: UIScreen.main.bounds)
let windowScene = UIApplication.shared
.connectedScenes
.filter { $0.activationState == .foregroundActive }
.first
if let windowScene = windowScene as? UIWindowScene {
window = UIWindow(windowScene: windowScene)
}
window.windowLevel = UIWindow.Level.alert - 1
window.isHidden = false
window.backgroundColor = .clear
let vc = UIViewController.init()
vc.view.backgroundColor = UIColor.black.withAlphaComponent(0.2)
window.rootViewController = vc
2、iOS 13及以后,若使用了SceneDelegate, 则在iOS13上获取 keyWindow 会得到nil,正确操作如下:
UIApplication.shared.windows.first
或者
var window: UIWindow?
if #available(iOS 13.0, *) {
window = (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window
} else {
window = UIApplication.shared.keyWindow
}
参考博客:
卧龙小
看影成痴
关于ios 13使用makeKeyAndVisible无效的问题
iOS 13 Scene Delegate and multiple windows