Article / 2026/05/29
UIViewController 完整指南
UIViewController 是 iOS 开发中最重要的类之一,它是所有视图控制器的基类。本指南详细介绍了 UIViewController 的所有主要 API、属性和用法。
🚀 UIViewController 完整指南
UIViewController 是 iOS 开发中最重要的类之一,它是所有视图控制器的基类。本指南详细介绍了 UIViewController 的所有主要 API、属性和用法。
目录
基础概念
UIViewController 是 MVC 架构中的控制器(Controller)部分,负责:
- 管理视图层次结构
- 处理用户交互
- 协调模型和视图之间的数据传递
- 管理视图控制器的生命周期
生命周期方法
主要生命周期方法
class MyViewController: UIViewController {
// 1. 视图加载到内存中后调用(只调用一次)
override func viewDidLoad() {
super.viewDidLoad()
// 设置一次性的配置
// 创建 UI 元素
// 设置观察者
}
// 2. 视图即将出现
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 刷新数据
// 更新 UI 状态
// 注册通知
}
// 3. 视图已经出现
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// 开始定时器
// 开始动画
// 开始位置服务等
}
// 4. 视图即将消失
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// 保存用户输入
// 暂停定时器
}
// 5. 视图已经消失
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// 停止定时器
// 清理资源
// 注销通知
}
// 6. 视图控制器从内存中释放
deinit {
// 清理观察者
// 释放资源
print("ViewController 被释放")
}
}
视图布局相关生命周期
// 视图即将布局子视图
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
// 在自动布局之前进行手动调整
}
// 视图已经布局子视图
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// 根据最终布局结果进行调整
// 更新基于frame的计算
}
// 当视图控制器的视图需要更新约束时调用
override func updateViewConstraints() {
super.updateViewConstraints()
// 更新约束
}
核心属性
视图相关属性
class MyViewController: UIViewController {
// 视图控制器的根视图
var view: UIView! { get set }
// 视图是否已加载
var isViewLoaded: Bool { get }
// 视图的安全区域
var additionalSafeAreaInsets: UIEdgeInsets { get set }
// 系统安全区域
var systemMinimumLayoutMargins: NSDirectionalEdgeInsets { get set }
// 视图边距
var viewRespectsSystemMinimumLayoutMargins: Bool { get set }
}
层次结构属性
// 父视图控制器
var parent: UIViewController? { get }
// 子视图控制器数组
var children: [UIViewController] { get }
// 展示此视图控制器的视图控制器
var presentingViewController: UIViewController? { get }
// 被此视图控制器展示的视图控制器
var presentedViewController: UIViewController? { get }
标识和信息属性
// 视图控制器标题
var title: String? { get set }
// 导航项配置
var navigationItem: UINavigationItem { get }
// 工具栏项
var toolbarItems: [UIBarButtonItem]? { get set }
// 标签栏项
var tabBarItem: UITabBarItem! { get set }
// 是否定义展示上下文
var definesPresentationContext: Bool { get set }
// 是否提供展示上下文
var providesPresentationContextTransitionStyle: Bool { get set }
视图管理
手动创建视图
class CustomViewController: UIViewController {
override func loadView() {
// 不要调用 super.loadView()
// 创建根视图
view = UIView()
view.backgroundColor = .white
// 添加子视图
let label = UILabel()
label.text = "Hello World"
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
// 设置约束
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
}
}
子视图控制器管理
class ContainerViewController: UIViewController {
// 添加子视图控制器
func addChildViewController(_ childController: UIViewController) {
// 1. 添加为子视图控制器
addChild(childController)
// 2. 添加子视图控制器的视图
view.addSubview(childController.view)
// 3. 设置约束或frame
childController.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
childController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
childController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
childController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
childController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
// 4. 通知子视图控制器已移动到父视图控制器
childController.didMove(toParent: self)
}
// 移除子视图控制器
func removeChildViewController(_ childController: UIViewController) {
// 1. 通知即将从父视图控制器移除
childController.willMove(toParent: nil)
// 2. 从父视图控制器中移除
childController.removeFromParent()
// 3. 从视图层次结构中移除
childController.view.removeFromSuperview()
}
}
导航相关
导航控制器集成
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 设置导航栏标题
title = "我的页面"
navigationItem.title = "导航标题"
// 设置导航栏按钮
navigationItem.rightBarButtonItem = UIBarButtonItem(
barButtonSystemItem: .add,
target: self,
action: #selector(addButtonTapped)
)
navigationItem.leftBarButtonItem = UIBarButtonItem(
title: "返回",
style: .plain,
target: self,
action: #selector(backButtonTapped)
)
// 隐藏导航栏
navigationController?.setNavigationBarHidden(true, animated: true)
// 设置大标题
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .always
}
@objc func addButtonTapped() {
// 导航到新页面
let nextVC = NextViewController()
navigationController?.pushViewController(nextVC, animated: true)
}
@objc func backButtonTapped() {
// 返回上一页
navigationController?.popViewController(animated: true)
}
}
导航相关属性和方法
// 导航控制器
var navigationController: UINavigationController? { get }
// 是否隐藏底部栏
var hidesBottomBarWhenPushed: Bool { get set }
// 扩展边缘
var edgesForExtendedLayout: UIRectEdge { get set }
// 是否扩展到不透明栏下方
var extendedLayoutIncludesOpaqueBars: Bool { get set }
// 自动调整滚动视图内边距
var automaticallyAdjustsScrollViewInsets: Bool { get set }
模态展示
展示模态视图控制器
class PresentingViewController: UIViewController {
func presentModalViewController() {
let modalVC = ModalViewController()
// 设置展示样式
modalVC.modalPresentationStyle = .pageSheet
modalVC.modalTransitionStyle = .coverVertical
// 展示
present(modalVC, animated: true) {
print("模态视图已展示")
}
}
// 也可以使用更详细的方法
func presentWithCustomTransition() {
let modalVC = ModalViewController()
modalVC.modalPresentationStyle = .custom
modalVC.transitioningDelegate = self
present(modalVC, animated: true, completion: nil)
}
}
// 关闭模态视图
class ModalViewController: UIViewController {
@IBAction func dismissButtonTapped(_ sender: UIButton) {
dismiss(animated: true) {
print("模态视图已关闭")
}
}
// 或者通过展示者关闭
func dismissFromPresenting() {
presentingViewController?.dismiss(animated: true, completion: nil)
}
}
模态展示样式
// 展示样式
enum UIModalPresentationStyle {
case fullScreen // 全屏
case pageSheet // 页面表单(iOS 13+默认)
case formSheet // 表单
case currentContext // 当前上下文
case custom // 自定义
case overFullScreen // 覆盖全屏
case overCurrentContext // 覆盖当前上下文
case popover // 弹出框(iPad)
case blurOverFullScreen // 模糊背景全屏
}
// 转场动画样式
enum UIModalTransitionStyle {
case coverVertical // 垂直覆盖(默认)
case flipHorizontal // 水平翻转
case crossDissolve // 交叉溶解
case partialCurl // 部分卷曲
}
状态栏管理
class MyViewController: UIViewController {
// 状态栏样式
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent // 或 .default, .darkContent
}
// 是否隐藏状态栏
override var prefersStatusBarHidden: Bool {
return false
}
// 状态栏更新动画
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .fade // 或 .slide, .none
}
// 更新状态栏外观
func updateStatusBar() {
setNeedsStatusBarAppearanceUpdate()
}
}
自动旋转
class MyViewController: UIViewController {
// 支持的界面方向
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait // 只支持竖屏
// return .landscape // 只支持横屏
// return .all // 支持所有方向
// return [.portrait, .landscapeLeft] // 支持多个方向
}
// 是否应该自动旋转
override var shouldAutorotate: Bool {
return true
}
// 首选的界面方向
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
return .portrait
}
// 旋转开始前调用
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { context in
// 在旋转动画期间执行的代码
self.updateLayoutForSize(size)
}) { context in
// 旋转动画完成后执行的代码
print("旋转完成")
}
}
private func updateLayoutForSize(_ size: CGSize) {
// 根据新尺寸更新布局
}
}
键盘管理
class KeyboardViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var bottomConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// 注册键盘通知
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillShow),
name: UIResponder.keyboardWillShowNotification,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(keyboardWillHide),
name: UIResponder.keyboardWillHideNotification,
object: nil
)
// 设置点击手势隐藏键盘
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tapGesture)
}
@objc func keyboardWillShow(notification: NSNotification) {
guard let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
let animationDuration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else {
return
}
bottomConstraint.constant = keyboardSize.height
UIView.animate(withDuration: animationDuration) {
self.view.layoutIfNeeded()
}
}
@objc func keyboardWillHide(notification: NSNotification) {
guard let animationDuration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double else {
return
}
bottomConstraint.constant = 0
UIView.animate(withDuration: animationDuration) {
self.view.layoutIfNeeded()
}
}
@objc func dismissKeyboard() {
view.endEditing(true)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
}
内存管理
内存警告处理
class MyViewController: UIViewController {
// 收到内存警告时调用
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// 清理不必要的资源
// 释放缓存
// 移除不在屏幕上显示的视图
print("收到内存警告,开始清理资源")
}
}
资源清理
class ResourceViewController: UIViewController {
var timer: Timer?
var observation: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
// 创建定时器
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
// 定时器任务
}
// 创建观察者
observation = observe(\.view.frame) { object, change in
// 观察回调
}
}
deinit {
// 清理定时器
timer?.invalidate()
timer = nil
// 清理观察者
observation?.invalidate()
observation = nil
// 移除通知观察者
NotificationCenter.default.removeObserver(self)
print("资源已清理")
}
}
最佳实践
1. 生命周期方法使用指南
class BestPracticeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// ✅ 适合在这里做的事:
// - 创建和配置 UI 元素
// - 设置一次性的观察者
// - 初始化数据源
setupUI()
setupObservers()
setupInitialData()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// ✅ 适合在这里做的事:
// - 刷新数据
// - 更新 UI 状态
// - 注册临时通知
refreshData()
updateUIState()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// ✅ 适合在这里做的事:
// - 开始动画
// - 开始定时器
// - 开始位置服务
startAnimations()
startLocationServices()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// ✅ 适合在这里做的事:
// - 保存用户数据
// - 验证输入
// - 暂停任务
saveUserInput()
validateInput()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// ✅ 适合在这里做的事:
// - 停止定时器
// - 停止网络请求
// - 清理临时资源
stopTimers()
cancelNetworkRequests()
}
}
2. 避免常见错误
class CommonMistakesViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// ❌ 错误:在 viewDidLoad 中进行网络请求
// fetchDataFromServer() // 不要这样做
// ❌ 错误:在 viewDidLoad 中更新依赖视图尺寸的布局
// updateFrameBasedLayout() // 不要这样做
// ✅ 正确:设置基础配置
setupBasicConfiguration()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// ✅ 正确:在这里进行数据刷新
fetchDataFromServer()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// ✅ 正确:在这里进行基于frame的布局更新
updateFrameBasedLayout()
}
// ❌ 错误:忘记调用 super
override func viewWillAppear(_ animated: Bool) {
// super.viewWillAppear(animated) // 必须调用!
// 自己的代码...
}
}
3. 性能优化技巧
class PerformanceViewController: UIViewController {
// 使用懒加载
lazy var expensiveView: UIView = {
let view = UIView()
// 复杂的初始化逻辑
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
// ✅ 延迟创建昂贵的 UI 元素
// 只在需要时才创建
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// ✅ 使用异步加载避免阻塞主线程
DispatchQueue.main.async {
self.loadDataAsynchronously()
}
}
private func loadDataAsynchronously() {
// 异步数据加载
}
// ✅ 合理使用缓存
private var dataCache: [String: Any] = [:]
private func loadData(for key: String) -> Any? {
if let cachedData = dataCache[key] {
return cachedData
}
let data = expensiveDataOperation(for: key)
dataCache[key] = data
return data
}
private func expensiveDataOperation(for key: String) -> Any {
// 昂贵的数据操作
return "data"
}
}
4. 内存安全
class MemorySafeViewController: UIViewController {
var networkTask: URLSessionDataTask?
override func viewDidLoad() {
super.viewDidLoad()
// ✅ 使用弱引用避免循环引用
networkTask = URLSession.shared.dataTask(with: URL(string: "https://api.example.com")!) { [weak self] data, response, error in
DispatchQueue.main.async {
self?.handleNetworkResponse(data: data, response: response, error: error)
}
}
}
private func handleNetworkResponse(data: Data?, response: URLResponse?, error: Error?) {
// 处理网络响应
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// ✅ 取消未完成的任务
networkTask?.cancel()
}
deinit {
// ✅ 确保清理所有资源
networkTask?.cancel()
networkTask = nil
}
}
总结
UIViewController 是 iOS 开发的核心类,掌握其生命周期、属性和方法对于开发高质量的 iOS 应用至关重要。关键要点包括:
- 生命周期管理:合理利用各个生命周期方法
- 内存管理:避免循环引用,及时清理资源
- 性能优化:懒加载、异步处理、缓存策略
- 用户体验:流畅的转场动画、合理的界面布局
- 代码质量:遵循最佳实践,避免常见错误
通过深入理解和正确使用 UIViewController,可以构建出稳定、高效、用户体验良好的 iOS 应用。
Giscus 未启用:请在
src/site.config.ts中配置 repoId 与 categoryId。