ViewController — 本质,iOS中的屏幕控制器及其生命周期

作者: IT Sectr 发布日期: 2026-02-22 阅读时间: 7 分钟

UIViewController — iOS应用程序的核心类,管理屏幕及其内容。iPhone或iPad的每个屏幕都由一个ViewController管理,它协调显示、生命周期和导航。关于UIKit架构的更多信息,请参阅Apple官方文档

要点

  • UIViewController — UIKit中用于管理屏幕的基础类,拥有自己的生命周期
  • viewDidLoad — 调用一次,UI初始化和数据订阅的点
  • viewWillAppear — 屏幕即将可见,显示前更新数据
  • 生命周期包括五个方法:viewDidLoad、viewWillAppear、viewDidAppear、viewWillDisappear、viewDidDisappear
  • Massive View Controller — iOS的主要反模式,通过MVVM或Coordinator解决

什么是ViewController?

UIViewController — UIKit框架中的一个类,管理UIView层次结构并协调数据在屏幕上的显示。每个iOS应用程序至少包含一个ViewController — 窗口的根控制器。控制器处理屏幕旋转、屏幕间的转场以及生命周期事件。

iOS中的MVC(Model-View-Controller)架构正是通过UIViewController实现的:控制器从模型接收数据并更新视图。ViewController不是可视元素 — 它管理包含subview层次结构的view属性。根据Apple的数据(2026年),UIKit包含超过40个内置的UIViewController子类。

第一个iPhone SDK(2008年)包含具有三个生命周期方法的UIViewController。在18年间,Apple增加了对Container View Controller的支持、自适应呈现、用于自定义动画的UIViewControllerTransitioningDelegate以及iPad上的分屏模式。UIViewController仍然是UIKit应用程序的必需组件。

UIViewController的生命周期

UIViewController的生命周期 — 系统在创建、显示和隐藏屏幕时调用的方法序列。理解生命周期至关重要:代码放置不当会导致内存泄漏、不必要的网络请求和界面闪烁。

方法调用时机用途
viewDidLoad一次,view加载到内存后初始UI设置,Combine订阅
viewWillAppear屏幕出现之前数据更新,隐藏/显示导航栏
viewDidAppear屏幕出现之后启动动画,分析,更新相机
viewWillDisappear离开屏幕之前保存草稿,取消通知订阅
viewDidDisappear离开屏幕之后停止繁重进程,释放资源

屏幕出现时的调用顺序

首次显示屏幕时:init → loadView → viewDidLoad → viewWillAppear → viewDidAppear。重新出现时(从其他屏幕返回):viewWillAppear → viewDidAppear。viewDidLoad在控制器的生命周期中只调用一次。

viewDidLoad、init和UI设置

viewDidLoad方法 — 用户界面配置的主要点。在view加载到内存后调用,此时所有IBOutlet都已连接。在这里以编程方式创建UI元素、配置约束、加载初始数据。

swift
final class ProfileViewController: UIViewController {

    private let tableView = UITableView()
    private let viewModel = ProfileViewModel()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        bindViewModel()
    }

    private func setupUI() {
        view.addSubview(tableView)
        tableView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
        tableView.register(ProfileCell.self,
                         forCellReuseIdentifier: ProfileCell.reuseId)
    }

    private func bindViewModel() {
        viewModel.$user
            .receive(on: DispatchQueue.main)
            .sink { [weak self] user in
                self?.title = user.name
            }
            .store(in: &cancellables)
    }
}

在SwiftUI中,此代码等同于View的主体。但UIViewController提供了对生命周期和优化的完全控制。bindViewModel使用Combine进行响应式订阅 — 数据在模型更改时自动更新。

viewWillAppear和数据更新

viewWillAppear每次在屏幕出现之前调用,即使它已经在内存中。这是更新可能在其他屏幕上更改的数据的地方:重新加载列表、更新通知计数器、为特定屏幕配置导航栏。

swift
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    // 在此屏幕上隐藏导航栏
    navigationController?.setNavigationBarHidden(true, animated: animated)

    // 从其他屏幕返回时更新数据
    tableView.reloadData()
    badgeLabel.text = "\(CartManager.shared.itemCount)"
}

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    // 分析:仅在用户看到屏幕后
    AnalyticsService.shared.logScreenView("Profile")
}

viewDidLoad和viewWillAppear之间的区别至关重要:viewDidLoad执行一次,适用于静态配置;viewWillAppear每次显示时执行,适用于动态更新。将网络请求放在viewDidLoad中会导致返回屏幕时显示过时数据。

Container View Controller:UINavigationController和UITabBarController

Container View Controller — 管理一个或多个子ViewController的ViewController。Apple提供三个内置容器:UINavigationController(屏幕堆栈)、UITabBarController(标签页)和UISplitViewController(iPad的master-detail)。

UINavigationController以堆栈形式组织转场 — push添加屏幕,pop移除。UITabBarController在应用程序的独立部分之间切换。UISplitViewController在iPad上并排显示两个控制器,在iPhone上显示一个。开发者可以通过addChild创建自己的容器。

swift
// 自定义Container View Controller
final class ContainerViewController: UIViewController {

    private let sidebarVC = SidebarViewController()
    private let contentVC = ContentViewController()

    override func viewDidLoad() {
        super.viewDidLoad()

        // 添加子控制器
        addChild(sidebarVC)
        view.addSubview(sidebarVC.view)
        sidebarVC.didMove(toParent: self)

        addChild(contentVC)
        view.addSubview(contentVC.view)
        contentVC.didMove(toParent: self)
    }
}

正确使用Container View Controller需要按此顺序调用addChild、添加view和didMove(toParent:)。移除时 — willMove(toParent: nil)、removeFromSuperview、removeFromParent。违反顺序会导致内存泄漏。

通过MVVM和Coordinator解决Massive View Controller

Massive View Controller的问题 — 当UIViewController包含数百行代码,包含业务逻辑、网络请求、导航和UI代码时。Apple意识到这个问题,并推荐MVVM(Model-View-ViewModel)结合Coordinator来分离导航。

MVVM将业务逻辑从控制器移到ViewModel。Controller仅通过Combine或delegate将ViewModel与View连接。Coordinator将导航逻辑 — 控制器的创建和转场 — 分离到单独的类中。这种方法自2024年起已被纳入Apple的最佳实践。

swift
// Coordinator — 导航管理
protocol Coordinator {
    var childCoordinators: [Coordinator] { get set }
    func start()
}

final class MainCoordinator: Coordinator {

    var childCoordinators = [Coordinator]()
    private let navigationController: UINavigationController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func start() {
        let vc = ListViewController()
        vc.didSelectItem = { [weak self] item in
            self?.showDetail(item)
        }
        navigationController.pushViewController(vc, animated: false)
    }

    private func showDetail(_ item: Item) {
        let vc = DetailViewController(item: item)
        navigationController.pushViewController(vc, animated: true)
    }
}

UIViewController vs SwiftUI:何时选择什么

UIViewController和SwiftUI View之间的选择取决于项目的开始年份、定制要求以及最低支持的iOS版本。带有UIViewController的UIKit仍然是2020年之前启动的项目和具有深度界面定制的应用程序的基础。

SwiftUI适用于iOS 17+的新项目、标准界面和原型。然而,对于自定义转场、相机操作、MapKit、复杂的CALayer动画,需要UIViewController。Apple建议通过UIHostingController(UIKit中的SwiftUI)和UIViewRepresentable(SwiftUI中的UIKit)结合两种方法。

场景UIKit (UIViewController)SwiftUI (View)
自定义动画通过UIViewPropertyAnimator完全控制通过Animation受限
相机操作AVCaptureSession + UIViewPreview通过UIViewControllerRepresentable
CollectionViewUICollectionView + UICollectionViewLayoutLazyVGrid/LazyHGrid
iPad适配UISplitViewController + UITraitCollectionNavigationSplitView + sizeClass
开发速度较慢(手动布局)较快(声明式)

常见问题

UIViewController和UIView有什么区别?

UIViewController — 管理屏幕及其生命周期的控制器。UIView — 显示内容的视图。ViewController包含UIView层次结构,但其本身不是可视元素。一个控制器管理多个视图。

什么是Massive View Controller?

Massive View Controller — 一种反模式,当UIViewController包含太多逻辑:数据、导航、网络请求、动画。解决方案 — 将代码分离到单独的服务、协调器和ViewModel(MVVM)中。

如何在ViewController之间传递数据?

四种方式:通过prepare(for:sender:)(Segue)时的属性、通过Delegate、通过Closure、通过共享服务。对于弱耦合,使用Coordinator + Delegate或Combine。

什么是Container View Controller?

Container View Controller — 管理子ViewController的控制器。例如:UINavigationController、UITabBarController、UISplitViewController。父控制器通过addChild添加子控制器,在它们之间切换并管理它们的布局。

何时使用UIViewController代替SwiftUI View?

UIViewController — 用于复杂自定义动画、相机、地图、视频、具有自定义布局的UICollectionView。SwiftUI View — 用于iOS 13+的标准界面。允许通过UIHostingController进行组合。

总结

  • UIViewController — 用于管理屏幕、UIView层次结构和生命周期的UIKit核心类
  • 生命周期包括五个方法:viewDidLoad、viewWillAppear、viewDidAppear、viewWillDisappear、viewDidDisappear
  • viewDidLoad — 初始UI配置点,在控制器的生命周期中只调用一次
  • viewWillAppear — 每次显示前调用,适用于数据更新和导航栏配置
  • Container View Controller(UINavigationController、UITabBarController)管理子控制器的层次结构
  • Massive View Controller通过MVVM(将逻辑分离到ViewModel)和Coordinator(分离导航)解决
  • UIViewController和SwiftUI可以通过UIHostingController和UIViewRepresentable结合用于混合应用

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读