UIKit 传统 iOS 开发:视图控制器与 Storyboard
UIKit 传统 iOS 开发:视图控制器与 Storyboard 完全指南
UIKit 是每一位 iOS 开发者必须掌握的基础框架。对于刚接触苹果生态的开发新手,理解视图控制器(View Controller)和 Storyboard 是构建传统 iOS 应用的第一步。本教程将带你从零开始,掌握如何使用 UIKit 和 Storyboard 搭建具有多页面的完整应用。
什么是 UIKit?
UIKit(User Interface Kit)是苹果为 iOS 和 tvOS 提供的界面框架,里面包含了所有用于构建图形化、事件驱动型应用的组件——按钮、标签、表格、导航栏等。只要是使用传统方式开发的 iOS 应用,底层几乎都离不开 UIKit。
UIKit 的核心设计模式是 MVC(Model-View-Controller),其中 View Controller 负责管理一个屏幕上的视图层级,处理用户交互,并协调数据模型。
理解视图控制器
视图控制器是 iOS 应用架构的骨架,每个可见的屏幕通常由一个视图控制器负责。
视图控制器的角色
- 管理视图:它持有一个
view属性,该视图作为整个屏幕的容器。 - 处理事件:响应按钮点击、手势、旋转等用户操作。
- 协调数据:从网络或本地读取数据,并将其传递给视图显示。
- 过渡与导航:控制界面跳转,例如 push、present。
简单地说,视图控制器 = 一个屏幕的“大脑”。
UIViewController 生命周期
当你使用 Storyboard 设计界面时,视图控制器的生命周期方法会在特定时刻被系统自动调用。理解这些方法能让你在正确的时机初始化数据、清理资源或调整布局。
| 方法 | 触发时机 | 常用用途 |
|---|---|---|
viewDidLoad() |
视图控制器第一次加载其视图层级时 | 一次性初始化:网络请求、设置代理、添加观察者 |
viewWillAppear(_:) |
视图即将显示在屏幕上 | 隐藏/显示导航栏、刷新数据、开始动画 |
viewDidAppear(_:) |
视图已经完全显示 | 开始视频播放、启动定位、弹出引导页 |
viewWillDisappear(_:) |
视图即将从屏幕移除 | 暂停音视频、保存编辑状态 |
viewDidDisappear(_:) |
视图已经完全不可见 | 停止耗时任务、关闭网络连接 |
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 仅执行一次:初始化界面、加载数据
print("视图加载完成")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 每次即将出现时执行
}
}
常用视图控制器类型
- UIViewController:最基本的独立屏幕容器,通常用于模态弹出或简单的页面。
- UINavigationController:栈式导航,提供导航栏和返回按钮,适合具有层级关系的页面流。
- UITabBarController:底部标签栏切换多个并行的功能模块。
- UITableViewController / UICollectionViewController:专注于列表或网格展示的控制器,内部整合了 UITableView 或 UICollectionView。
本教程重点关注 UIViewController 以及如何使用 Storyboard 将它们串联起来。
初识 Storyboard
Storyboard(故事板)是 Xcode 提供的可视化界面设计工具,让你可以用拖拽的方式构建整个应用的界面和屏幕之间的切换关系。
Storyboard 与 XIB
在 Storyboard 被广泛使用之前,每个视图控制器通常对应一个 .xib 文件,只能独立地设计单界面。而 Storyboard 可以将多个屏幕的设计组织在一个文件中,并直观地看到它们之间的 segues(过渡)。对于中小型项目,Storyboard 极大提升了开发效率。
使用 Interface Builder 构建界面
打开 Xcode,新建一个 iOS 项目并选择 Storyboard 模板,你会看到 Main.storyboard。中间空白区域就是 画布,右下角的组件库(Library)包含了所有 UIKit 控件。
- 拖拽一个
View Controller到画布上。 - 再拖拽一个
Button到视图中,并修改其标题。 - 选中视图控制器,在右侧 Identity Inspector 中将其 Class 指定为你自定义的
UIViewController子类,建立代码与 Storyboard 的关联。
添加控件与自动布局
为了让界面在不同尺寸的 iPhone 上都表现良好,你需要使用 Auto Layout(自动布局)。选中按钮,点击右下角的 “Add New Constraints” 按钮,设置好距离父视图边界的约束。建议遵守以下原则:
- 每个控件在水平和垂直方向上都需要足够确定其位置和尺寸。
- 使用 Stack View 可以快速对齐和分布一组控件。
- 对于动态内容,设置 内容拥抱优先级 和 抗压缩优先级 来避免布局冲突。
连接代码与界面
Storyboard 的界面设计需要和 Swift 代码关联,这通过 IBOutlet 和 IBAction 实现。
IBOutlet 与 IBAction
- IBOutlet:将界面上的控件(如 UILabel、UIButton)引用到代码中,以便你可以通过代码修改其属性。
- IBAction:将控件的触发事件(如点击按钮)连接到代码中的方法。
操作步骤:
- 打开 Assistant Editor,让 Storyboard 和对应的 ViewController.swift 并排显示。
- 按住
Control键,从控件拖一条线到代码中,选择 Connection 类型为 Outlet 或 Action。 - 命名后,Xcode 会自动生成类似下面的代码:
@IBOutlet weak var titleLabel: UILabel!
@IBAction func buttonTapped(_ sender: UIButton) {
print("按钮被点击")
}
视图控制器之间的 Segue
Segue 定义了从一个视图控制器到另一个视图控制器的过渡。你同样可以通过 Control + 拖拽从按钮或手势到目标控制器来创建:
- Show (Push):用于导航控制器的层级跳转。
- Present Modally:以模态形式弹出新控制器。
- Custom:自定义过渡动画。
在代码中,你可以重写 prepare(for:sender:) 方法,在跳转之前向目标控制器传递数据:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
let destinationVC = segue.destination as! DetailViewController
destinationVC.receivedText = "来自主页的数据"
}
}
传值给下一个控制器
除了使用 prepare(for:sender:),你也可以在目标控制器中定义一个属性,然后在源控制器中通过 destination 赋值。如果是模态弹出的情况,还可以使用 委托 或 闭包 进行反向传值。
动手实践:创建一个双页面应用
让我们通过一个完整的实操例子巩固所学内容:一个简单的信息展示应用,点击按钮后跳转到详情页并显示传入的文字。
设置 Storyboard
- 打开
Main.storyboard,删除默认的 View Controller。 - 拖入一个
Navigation Controller,它会自动带一个UITableViewController。我们将表视图控制器替换为普通UIViewController:删除表视图控制器,拖入新的 View Controller,按住 Control 从导航控制器拖线到新控制器,选择 “root view controller” 关系。 - 给根控制器添加一个
Button,标题设为 “查看详情”。 - 再拖入一个 View Controller 作为详情页,添加一个
Label居中用于显示文本。 - 从按钮 Control+拖线到详情页控制器,选择 “Show” segue。
- 选中刚刚创建的 segue,在 Attributes Inspector 中设置 Identifier 为 “goToDetail”。
编写代码实现跳转
创建两个 Swift 文件:HomeViewController 和 DetailViewController,并在 Storyboard 中将控制器的类名对应设置好。
HomeViewController.swift:
import UIKit
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
title = "主页"
}
@IBAction func goToDetailTapped(_ sender: UIButton) {
performSegue(withIdentifier: "goToDetail", sender: nil)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToDetail" {
let detailVC = segue.destination as! DetailViewController
detailVC.infoText = "从主页传来的信息"
}
}
}
DetailViewController.swift:
import UIKit
class DetailViewController: UIViewController {
@IBOutlet weak var infoLabel: UILabel!
var infoText: String?
override func viewDidLoad() {
super.viewDidLoad()
title = "详情"
if let text = infoText {
infoLabel.text = text
}
}
}
连接 IBOutlet 和 IBAction:
把 infoLabel 从 Label 拖线到 DetailViewController 的 outlet,把按钮的 Touch Up Inside 事件拖线到 goToDetailTapped 动作。或者直接从按钮建立 Show Segue,然后按上述代码调用 performSegue,两者选其一即可。
测试与运行
在模拟器或真机上运行应用,点击按钮,你应该看到详情页面显示 “从主页传来的信息”。通过导航栏的返回按钮可以回到主页。
这样,一个使用 Storyboard 和传统 UIKit 的多页面应用就完成了。
常见问题与最佳实践
- 视图控制器过于臃肿:避免将所有业务逻辑写在
viewDidLoad里。使用 MVVM 或协调器模式解耦,保持控制器职责单一。 - Storyboard 合并冲突:当团队多人修改同一个 Storyboard 时经常产生 Git 冲突。可以将不同模块拆分到多个 Storyboard 中,或考虑结合代码布局。
- 强引用循环:使用闭包传递回调时,注意使用
[weak self]避免循环引用。 - Segue 命名:为每个 segue 设置一个清晰唯一的 Identifier,并定义为常量管理,防止拼写错误。
- 视图加载时机:在
viewDidLoad中设置初始 UI,在viewWillAppear中更新动态数据,不要尝试在viewDidLoad中访问尚未加载的视图尺寸。
结语
UIKit 与 Storyboard 的组合为传统 iOS 开发提供了一套成熟且高效的工作流。理解视图控制器的生命周期和 Storyboard 的 segue 机制,是构建大多数 iOS 应用的基石。虽然现代开发中 SwiftUI 崭露头角,但掌握 UIKit 依然是深入 iOS 开发世界的必经之路。从今天开始,尝试用 Storyboard 搭建你自己的多屏幕应用吧。