iOS窗口容器UIWindow
iOS窗口容器UIWindow

iOS窗口容器UIWindow

UIWindow 是 iOS 应用中的窗口容器,它继承自 UIView,是所有可见 UI 的顶层容器。

在UIKit应用中,UIWindow负责:

1、管理视图的显示:所有的 UIViewController 的视图最终都添加到 UIWindow 上展示。

2、响应事件的传递入口:所有触摸、手势、键盘等事件都先传递到 UIWindow,再向下传递。

3、承载根视图控制器 (rootViewController):这是整个界面的起点,是用户能看到的第一个页面(通常是登录页、首页等)。

UIWindow结构层级

在 iOS 中,UIWindow 是用户界面呈现的最顶层视图容器,负责显示和管理整个 App 的界面。下面是 UIWindow 在整个视图结构中的层级关系图解:

1、UIApplication:类单例,管理整个App的生命周期、事件循环、场景管理,是App的总控制器。

2、UIWindow:UIView子类,显示 App 的可视界面,承载所有控制器的视图,响应事件调度。通常一个场景(Scene)对应一个 UIWindow。

3、UIViewController:控制器,管理一个界面(view),处理业务逻辑和视图状态。UIWindow 会将一个 UIViewController 设置为 rootViewController。

例如:

let window = UIWindow()
let homeVC = HomeViewController() // HomeViewController 是 UIViewController 的子类
window.rootViewController = homeVC

HomeViewController 是自定义的一个控制器(UIViewController的子类)。

window.rootViewController 是UIWindow的根控制器属性,在这里设置为 homeVC(自定义的控制器)。

4、view:视图,控制器所管理的界面内容区域,是最终显示在屏幕上的具体元素容器。可以嵌套多个子视图。

UIKit 渲染和事件流程

1、App 启动 → UIApplication 创建。

2、创建一个或多个 UIWindow(通常在 SceneDelegate 中)。

3、给 window.rootViewController 设置一个控制器(比如导航控制器,这里涉及UIViewController)。

4、UIViewController控制器加载其 view 并布局。

5、UIWindow 显示在屏幕上,负责分发触控事件到对应的视图。

如何使用 UIWindow?

通常不会直接创建 UIWindow,因为系统会自动处理。但在某些高级用法中(例如手动配置 App 生命周期或展示自定义弹窗),会这样用:

设置主窗口:

let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = MyMainViewController()
window.makeKeyAndVisible()
self.window = window

这个写法在 AppDelegate(旧项目)或 SceneDelegate(新项目)中都很常见。

根视图控制器rootViewController

rootViewController 是 UIWindow 的根视图控制器,也就是整个界面层级的起点。

例如:

let window = UIWindow()
window.rootViewController = UINavigationController(rootViewController: HomeViewController())

在上面这段代码中:

rootViewController 是 UINavigationController。

这个导航控制器内部包含了一个 HomeViewController。

后续 push、present 的所有控制器,都是从这里开始的。

rootViewController 就是整个界面的起始控制器,是 UIWindow 启动后最初展示的视图控制器,它类似于 SwiftUI 中的 NavigationStack 或 TabView 的“根视图”。后续展示的所有控制器(无论是 push、present,还是切换 tab)都是基于它来进行的,但它本身始终存在于内存中,不会被替换或销毁。

在这里主视图就是rootViewController,而Sheet弹出视图就可以理解为在主视图上使用present()出来的控制器。

因此,无论展示多少个模态控制器(如Sheet),rootViewController始终存在于最底层。

NavigationStack {
    ContentView()
        .sheet(isPresented: $showSheet) {
            SheetView()
        }
}

这里的ContentView类似于UIKit的rootViewController,而SheetView就是present()出来的控制器。

rootViewController的作用

在理解rootViewController和其他控制器之间的关系后,我们需要进一步了解rootViewController的作用。当我们需要操作界面时,需要一个“起点”,那就是rootViewController。

我们想要查看当前App的rootViewController:

if let windowScene = UIApplication.shared.connectedScenes
    .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene,
   let rootVC = windowScene.windows.first?.rootViewController {
    print("当前的 rootVC 是:\(type(of: rootVC))")
}

当我们需要进行视图交互时,我们就可以使用rootViewController。

1、弹出一个模态页面

let alert = UIAlertController(title: "提示", message: "操作成功", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "确定", style: .default))
rootVC.present(alert, animated: true)

2、推送导航控制器

如果 rootVC 是 UINavigationController:

if let nav = rootVC as? UINavigationController {
    nav.pushViewController(MyNextViewController(), animated: true)
}

3、嵌套 SwiftUI 视图

let hostingVC = UIHostingController(rootView: MySwiftUIView())
rootVC.present(hostingVC, animated: true)

4、检查是否已经有模态页面显示

if rootVC.presentedViewController == nil {
    rootVC.present(MyCustomVC(), animated: true)
}

rootViewController 是 UIWindow 中用来呈现和管理整个 UI 层级的“第一块砖”,所有的页面导航、弹窗等都从它开始。

此外rootViewController 是一个 UIViewController 类型的实例,它拥有 UIViewController 的所有属性和方法,能够管理视图以及生控制视图的显示、消失和加载等功能。

与SwiftUI的关系

在 SwiftUI 项目中,虽然看不到 UIWindow,但它依然存在于背后,由系统管理。可以通过以下方式访问:

UIApplication.shared.connectedScenes
    .compactMap { $0 as? UIWindowScene }
    .flatMap { $0.windows }

或者用 SwiftUI 提供的 UIWindow-桥接方式来获取:

if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
    let window = windowScene.windows.first
}

或者

guard let windowScene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene else {
    return
}

UIApplication.shared.connectedScenes 是什么?

这是一个 Set<UIScene> 类型,表示当前 App 所有“已连接”的场景(Scene),这些场景可能是:

1、UIWindowScene(用得最多的,代表 UI 界面)。

2、其他类型的 UIScene(理论上支持,但很少见,目前几乎都只有 UIWindowScene)。

是在过滤出真正用于 UI 显示的场景。

activationState == .foregroundActive 的作用是什么?

这个是用于判断每个 UIWindowScene 的当前状态:

1、.foregroundActive: 正在前台活动(用户当前正在看)。

2、.foregroundInactive: 前台但不活跃(比如有电话进来)。

3、.background: 已经进入后台。

4、.unattached: 尚未连接(极少出现)。

iPhone 场景

正常情况下只会有一个 foregroundActive 的 UIWindowScene。

iPad 场景(多窗口)

可以有多个 UIWindowScene 是 .foregroundActive。

比如Split View(分栏)或 Slide Over(滑出窗口)。

UIApplication.shared.connectedScenes
    .compactMap { $0 as? UIWindowScene }
    .filter { $0.activationState == .foregroundActive }

这个可以拿到多个活跃窗口。

windowScene.windows.first 是什么?

这是从 UIWindowScene 中拿到窗口列表(类型 [UIWindow])的第一个:

let window: UIWindow? = windowScene.windows.first

是 UIWindowScene 管理的多个窗口中的第一个,通常是主窗口(key window)。

注意:它不一定是正在显示的最上层 window(需要看 windowLevel)。

常见使用场景

1、多窗口支持(iPad):iOS 13+ 引入了多窗口,意味着一个 app 可以有多个 UIWindow(每个 Scene 一份)。

2、自定义浮层 / Toast 弹窗:通常用新的 UIWindow 创建浮层(类似微信语音提示)。

3、启动页、引导页:在正式界面加载前,通过 UIWindow 提前显示 logo 页面或引导动画。

与生命周期的关系

UIWindow 是生命周期开始的重要入口,但它本身没有生命周期方法(没有像 viewDidLoad()、viewWillAppear() 这些)。

它的主要作用是触发下面这些内容:

1、AppDelegate的application(_:didFinishLaunchingWithOptions:)方法,用于应用启动,系统创建 UIWindow。

2、SceneDelegate的scene(_:willConnectTo:)方法,每个scene 对应一个 UIWindow。

3、UIViewController的viewDidLoad / viewWillAppear方法,界面生命周期的入口,从 UIWindow 中被加载时触发。

所以:UIWindow 是生命周期的触发器,不是生命周期的拥有者。

总结

UIWindow 是应用的最顶层 UI 容器;

所有可见视图最终都会加在 UIWindow 上;

SwiftUI 虽然隐藏了它,但实际上仍依赖 UIWindow 来展示内容;

特殊场景下(如浮层、独立弹窗、主界面重建)可以手动使用或替换 UIWindow。

相关文章

1、iOS UIApplication类:https://fangjunyu.com/2025/05/20/ios-uiapplication%e7%b1%bb/

2、SwiftUI和iOS核心类UIViewController:https://fangjunyu.com/2025/05/19/swiftui%e5%92%8cios%e6%a0%b8%e5%bf%83%e7%b1%bbuiviewcontroller/

3、iOS支持多窗口的UIScene:https://fangjunyu.com/2025/05/21/ios%e6%94%af%e6%8c%81%e5%a4%9a%e7%aa%97%e5%8f%a3%e7%9a%84uiscene/

4、iOS界面UIView:https://fangjunyu.com/2025/05/20/ios%e7%95%8c%e9%9d%a2uiview/

5、iOS窗口容器UIWindow:https://fangjunyu.com/2025/05/20/ios%e7%aa%97%e5%8f%a3%e5%ae%b9%e5%99%a8uiwindow/

扩展知识

什么是模态视图(Modal View Controller)

模态视图(Modal View Controller)指的是:

一种“覆盖”当前视图的方式,用来临时展示另一个视图控制器,直到用户关闭它**(dismiss)为止。

可以把它理解成弹窗、页面叠加,但用户不能继续操作底部视图,必须处理当前弹出的内容。

示例场景:

1、登录弹窗

2、设置页弹出

3、操作确认(如提示保存)

模态控制器的叠加

模态控制器(Modal View Controller)在 iOS 中不是严格只能显示一个,但系统在设计和用户体验上鼓励一次只展示一个模态控制器。

多个模态视图是怎么叠加的?

rootVC.present(AViewController, animated: true)
// 然后在 AViewController 中再 present BViewController

例如,在这个图示中:

1、rootViewController显示一个present模态页面AViewController。

2、AViewController显示一个present模态页面BViewController。

可以递归查看模态链:

var topController = rootVC
while let presented = topController.presentedViewController {
    topController = presented
}

这段代码就是为了找到当前屏幕上最顶层的模态控制器,通常用于在最安全的地方 present 新控制器。

presentedViewController获取的是哪个模态控制器?

当我们直接访问:

let presented = rootVC.presentedViewController

那 presented 获取的是 AViewController。

这时因为presentedViewController 是当前控制器“直接”呈现(present)的下一个控制器。

1、rootVC.presentedViewController显示的是AViewController。

2、AViewController.presentedViewController显示的是 BViewController。

3、BViewController.presentedViewController,如果还有的话,是下一个…

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注