UIKit 传统 iOS 开发:视图控制器与 Storyboard

FreeGuideOnline 最新 2026-06-17

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 控件。

  1. 拖拽一个 View Controller 到画布上。
  2. 再拖拽一个 Button 到视图中,并修改其标题。
  3. 选中视图控制器,在右侧 Identity Inspector 中将其 Class 指定为你自定义的 UIViewController 子类,建立代码与 Storyboard 的关联。

添加控件与自动布局

为了让界面在不同尺寸的 iPhone 上都表现良好,你需要使用 Auto Layout(自动布局)。选中按钮,点击右下角的 “Add New Constraints” 按钮,设置好距离父视图边界的约束。建议遵守以下原则:

  • 每个控件在水平和垂直方向上都需要足够确定其位置和尺寸。
  • 使用 Stack View 可以快速对齐和分布一组控件。
  • 对于动态内容,设置 内容拥抱优先级抗压缩优先级 来避免布局冲突。

连接代码与界面

Storyboard 的界面设计需要和 Swift 代码关联,这通过 IBOutlet 和 IBAction 实现。

IBOutlet 与 IBAction

  • IBOutlet:将界面上的控件(如 UILabel、UIButton)引用到代码中,以便你可以通过代码修改其属性。
  • IBAction:将控件的触发事件(如点击按钮)连接到代码中的方法。

操作步骤:

  1. 打开 Assistant Editor,让 Storyboard 和对应的 ViewController.swift 并排显示。
  2. 按住 Control 键,从控件拖一条线到代码中,选择 Connection 类型为 Outlet 或 Action。
  3. 命名后,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

  1. 打开 Main.storyboard,删除默认的 View Controller。
  2. 拖入一个 Navigation Controller,它会自动带一个 UITableViewController。我们将表视图控制器替换为普通 UIViewController:删除表视图控制器,拖入新的 View Controller,按住 Control 从导航控制器拖线到新控制器,选择 “root view controller” 关系。
  3. 给根控制器添加一个 Button,标题设为 “查看详情”。
  4. 再拖入一个 View Controller 作为详情页,添加一个 Label 居中用于显示文本。
  5. 从按钮 Control+拖线到详情页控制器,选择 “Show” segue。
  6. 选中刚刚创建的 segue,在 Attributes Inspector 中设置 Identifier 为 “goToDetail”。

编写代码实现跳转

创建两个 Swift 文件:HomeViewControllerDetailViewController,并在 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 搭建你自己的多屏幕应用吧。