iOS UI 开发完全指南:UIKit 与 SwiftUI

iOS UI 开发完全指南:UIKit 与 SwiftUI

📖 目录

  1. 概览
  2. [UIKit 框架详解](#UIKit 框架详解)
  3. [SwiftUI 框架详解](#SwiftUI 框架详解)
  4. 对比分析
  5. 选型建议
  6. 总结

🎯 概览

iOS 界面开发主要分为两大阵营:

维度 UIKit SwiftUI
推出时间 iOS 2.0 (2008) iOS 13.0 (2019)
编程范式 命令式 (Imperative) 声明式 (Declarative)
当前状态 成熟稳定,维护模式 快速发展,Apple 主推
最低支持 iOS 2.0+ iOS 13.0+

🧱 UIKit 框架详解

1. 核心概念与架构

📐 UIKit 是什么?

UIKit 是 iOS 开发的基础 UI 框架,提供了一套完整的命令式编程接口来构建用户界面。它是 iOS 应用界面开发的基石。

🔗 UIKit、Storyboard、UIViewController 的关系

提供基础组件
提供控制器基类
加载/管理
描述布局
通过连线
运行时创建
渲染为
UIKit 框架
UIView/UIButton/UILabel...
UIViewController
Storyboard 文件
XML 配置
用户界面

三者关系总结:

组件 角色 本质 类比
UIKit 基础设施 框架 (Framework) 建筑材料库
UIViewController 控制器 类 (Class) 施工监理 + 骨架
Storyboard 布局文件 XML 文件 可视化设计图纸

协同工作流程:

swift 复制代码
// 1. 创建 UIViewController 子类
class MyViewController: UIViewController {
    @IBOutlet weak var label: UILabel!  // 从 Storyboard 连线
    
    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = "Hello UIKit"
    }
    
    @IBAction func buttonTapped(_ sender: UIButton) {
        label.text = "按钮被点击了"
    }
}

// 2. 在 Storyboard 中拖拽 UI 元素并连线到上述代码
// 3. App 运行时,UIViewController 加载 Storyboard,用 UIKit 创建真实视图

2. UIKit 主要组件分类

📦 基础视图组件
组件 用途 常用场景
UIView 所有视图的基类 容器、背景
UIButton 按钮 用户点击操作
UILabel 文本标签 显示静态文字
UITextField 单行文本输入 表单、登录框
UITextView 多行文本输入 备注、评论
UIImageView 图片展示 头像、配图
UIScrollView 滚动视图 内容超出屏幕
UITableView 列表视图 消息列表、设置页
UICollectionView 集合视图 相册、瀑布流
UISwitch 开关 设置开关
UISlider 滑块 音量、进度调节
UIProgressView 进度条 加载进度
🎯 容器控制器
容器 用途 使用场景
UINavigationController 导航栈管理 层级页面跳转
UITabBarController Tab 标签页 底部导航栏
UISplitViewController 分屏视图 iPad 左右布局
UIPageViewController 翻页效果 电子书、引导页

3. UIViewController 生命周期

swift 复制代码
class LifecycleViewController: UIViewController {
    
    // 视图加载完成(只调用一次)
    override func viewDidLoad() {
        super.viewDidLoad()
        print("1. 视图已加载")
        // 初始化数据、注册通知、设置 delegates
    }
    
    // 视图即将出现
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("2. 视图即将出现")
        // 每次页面显示前都会调用,适合刷新数据
    }
    
    // 视图已经出现
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("3. 视图已出现")
        // 启动动画、开启定时器
    }
    
    // 视图即将消失
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("4. 视图即将消失")
        // 保存数据、停止动画
    }
    
    // 视图已经消失
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        print("5. 视图已消失")
        // 停止网络请求、移除通知
    }
}

4. UIKit 的三种 UI 构建方式

方式一:Storyboard(推荐新手/原型)
swift 复制代码
// ViewController.swift
class LoginViewController: UIViewController {
    @IBOutlet weak var usernameTextField: UITextField!
    @IBOutlet weak var passwordTextField: UITextField!
    @IBOutlet weak var loginButton: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        loginButton.layer.cornerRadius = 8
    }
    
    @IBAction func loginButtonTapped(_ sender: UIButton) {
        let username = usernameTextField.text ?? ""
        let password = passwordTextField.text ?? ""
        print("登录: \(username)/\(password)")
    }
}
方式二:XIB(单个视图复用)
swift 复制代码
// CustomCardView.swift
class CustomCardView: UIView {
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var contentLabel: UILabel!
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        loadFromNib()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        loadFromNib()
    }
    
    private func loadFromNib() {
        let nib = UINib(nibName: "CustomCardView", bundle: nil)
        let view = nib.instantiate(withOwner: self).first as! UIView
        view.frame = bounds
        addSubview(view)
    }
}
方式三:纯代码(最高灵活性)
swift 复制代码
class CodeOnlyViewController: UIViewController {
    
    private let titleLabel: UILabel = {
        let label = UILabel()
        label.text = "纯代码界面"
        label.font = .systemFont(ofSize: 24, weight: .bold)
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        return label
    }()
    
    private let actionButton: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("点击我", for: .normal)
        button.backgroundColor = .systemBlue
        button.setTitleColor(.white, for: .normal)
        button.layer.cornerRadius = 8
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        setupViews()
        setupConstraints()
        setupActions()
    }
    
    private func setupViews() {
        view.addSubview(titleLabel)
        view.addSubview(actionButton)
    }
    
    private func setupConstraints() {
        NSLayoutConstraint.activate([
            titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 100),
            
            actionButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            actionButton.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 50),
            actionButton.widthAnchor.constraint(equalToConstant: 200),
            actionButton.heightAnchor.constraint(equalToConstant: 44)
        ])
    }
    
    private func setupActions() {
        actionButton.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
    }
    
    @objc private func buttonTapped() {
        titleLabel.text = "按钮被点击了"
    }
}

5. UIKit 使用场景

场景 推荐程度 理由
维护 5 年以上老项目 ⭐⭐⭐⭐⭐ 历史代码量大,迁移成本高
支持 iOS 13 以下版本 ⭐⭐⭐⭐⭐ SwiftUI 最低要求 iOS 13
复杂自定义动画 ⭐⭐⭐⭐⭐ Core Animation 成熟稳定
超长列表(1000+ 条) ⭐⭐⭐⭐ UITableView 复用机制优秀
地图/视频编辑等高性能场景 ⭐⭐⭐⭐⭐ 底层 Metal 集成完善
高度定制 UI 控件 ⭐⭐⭐⭐⭐ 完全控制绘制过程
新项目(无历史包袱) ⭐⭐ Apple 主推 SwiftUI

6. UIKit 注意事项 ⚠️

  1. Auto Layout 调试困难:约束错误信息不直观

    swift 复制代码
    // 调试约束冲突
    view.window?.constraints.forEach { print($0) }
  2. Storyboard 合并冲突:XML 格式难以手动合并

    • 建议:大型团队分模块使用多个 Storyboard
  3. 内存管理:循环引用常见

    swift 复制代码
    // 错误示例
    button.addTarget(self, action: #selector(method), for: .touchUpInside)
    
    // 正确示例
    button.addTarget(self, action: #selector(method), for: .touchUpInside)
    // 需要在 deinit 中移除或使用 weak
  4. 主线程 UI 更新:必须在主线程

    swift 复制代码
    DispatchQueue.main.async {
        self.label.text = "更新"
    }

🚀 SwiftUI 框架详解

1. 核心概念与架构

📐 SwiftUI 是什么?

SwiftUI 是 Apple 于 2019 年推出的声明式UI 框架,允许开发者用简洁的代码描述界面应该呈现的样子,框架自动处理状态变化时的视图更新。

🔗 SwiftUI 的核心组件

渲染错误: Mermaid 渲染失败: Parse error on line 10: ...ack] C --> H[@State] C --> ---------------------^ Expecting 'AMP', 'COLON', 'PIPE', 'TESTSTR', 'DOWN', 'DEFAULT', 'NUM', 'COMMA', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'LINK_ID'

与 UIKit 的根本差异:

swift 复制代码
// UIKit - 命令式:告诉系统"怎么做"
var count = 0
label.text = "\(count)"  // 手动设置

func increment() {
    count += 1
    label.text = "\(count)"  // 又要手动更新
}

// SwiftUI - 声明式:告诉系统"是什么"
@State var count = 0

var body: some View {
    Text("\(count)")  // 描述关系
    Button("增加") { count += 1 }  // 修改状态,UI 自动更新
}

2. 状态管理

属性包装器 用途 作用域 示例
@State 视图本地状态 当前 View 点击计数、开关状态
@Binding 双向绑定 父子视图传递 表单输入
@ObservedObject 外部可观察对象 跟随视图生命周期 ViewModel
@StateObject 创建并持有 ObservableObject 视图生命周期 数据模型
@EnvironmentObject 全局共享状态 整个视图树 用户登录状态
@Environment 系统环境值 当前视图 颜色模式、大小类

示例演示:

swift 复制代码
// 1. 定义数据模型
class UserViewModel: ObservableObject {
    @Published var username = ""
    @Published var isLoggedIn = false
}

// 2. 主视图
struct ContentView: View {
    @StateObject private var viewModel = UserViewModel()
    @State private var showSettings = false
    @Environment(\.colorScheme) var colorScheme  // 系统环境
    
    var body: some View {
        NavigationStack {
            VStack {
                if viewModel.isLoggedIn {
                    ProfileView()
                        .environmentObject(viewModel)  // 传递到子视图
                } else {
                    LoginView(username: $viewModel.username)  // 双向绑定
                }
                
                Text("当前主题: \(colorScheme == .dark ? "深色" : "浅色")")
            }
            .sheet(isPresented: $showSettings) {
                SettingsView()
            }
        }
    }
}

// 3. 子视图 - 使用 @Binding
struct LoginView: View {
    @Binding var username: String
    @State private var password = ""
    
    var body: some View {
        TextField("用户名", text: $username)
        SecureField("密码", text: $password)
    }
}

// 4. 子视图 - 使用 @EnvironmentObject
struct ProfileView: View {
    @EnvironmentObject var viewModel: UserViewModel
    
    var body: some View {
        Text("欢迎, \(viewModel.username)!")
        Button("退出") {
            viewModel.isLoggedIn = false
        }
    }
}

3. 布局系统

SwiftUI 使用 Stack 布局 替代 Auto Layout:

容器 作用 类比 UIKit
VStack 垂直排列 UIStackView axis=.vertical
HStack 水平排列 UIStackView axis=.horizontal
ZStack 层叠排列 多个视图叠加
LazyVStack 懒加载垂直列表 优化的垂直列表
LazyHStack 懒加载水平列表 优化的水平列表
Grid 网格布局 UICollectionView
Form 表单布局 分组 TableView

布局示例:

swift 复制代码
struct LayoutDemoView: View {
    var body: some View {
        ScrollView {
            // 垂直布局
            VStack(alignment: .leading, spacing: 16) {
                // 水平布局
                HStack {
                    Image(systemName: "person.circle.fill")
                        .font(.largeTitle)
                    Text("用户信息")
                        .font(.title2)
                        .bold()
                    Spacer()  // 弹性空间
                    Button("编辑") { }
                }
                .padding()
                
                // ZStack 层叠
                ZStack(alignment: .topTrailing) {
                    RoundedRectangle(cornerRadius: 12)
                        .fill(Color.gray.opacity(0.2))
                        .frame(height: 200)
                    
                    Text("新")
                        .font(.caption)
                        .padding(4)
                        .background(Color.red)
                        .foregroundColor(.white)
                        .clipShape(Circle())
                        .offset(x: -8, y: 8)
                }
                
                // 网格布局
                LazyVGrid(columns: [
                    GridItem(.flexible()),
                    GridItem(.flexible()),
                    GridItem(.flexible())
                ], spacing: 8) {
                    ForEach(0..<9) { index in
                        Rectangle()
                            .fill(Color.blue.opacity(0.3))
                            .aspectRatio(1, contentMode: .fit)
                            .overlay(Text("\(index)"))
                    }
                }
            }
            .padding()
        }
    }
}

4. 常用组件示例

基础组件
swift 复制代码
import SwiftUI

struct ComponentDemoView: View {
    @State private var text = ""
    @State private var isOn = false
    @State private var sliderValue = 0.5
    
    var body: some View {
        List {
            // 文本
            Text("普通文本")
            Text("自定义样式")
                .font(.title)
                .foregroundColor(.blue)
                .bold()
            
            // 图片
            Image(systemName: "heart.fill")
                .foregroundColor(.red)
                .font(.largeTitle)
            
            // 按钮
            Button("普通按钮") { print("点击") }
            Button(action: { print("自定义按钮") }) {
                Label("自定义", systemImage: "star.fill")
                    .padding()
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .cornerRadius(8)
            }
            
            // 输入框
            TextField("请输入用户名", text: $text)
                .textFieldStyle(.roundedBorder)
            
            // 开关
            Toggle("开启通知", isOn: $isOn)
            
            // 滑块
            Slider(value: $sliderValue, in: 0...1)
            Text("进度: \(Int(sliderValue * 100))%")
            
            // 进度条
            ProgressView(value: sliderValue)
        }
    }
}
导航与列表
swift 复制代码
struct NavigationListDemoView: View {
    let items = ["消息", "通讯录", "发现", "我的"]
    
    var body: some View {
        NavigationStack {
            List(items, id: \.self) { item in
                NavigationLink(destination: DetailView(title: item)) {
                    HStack {
                        Image(systemName: "folder")
                        Text(item)
                    }
                }
            }
            .navigationTitle("首页")
            .toolbar {
                ToolbarItem(placement: .topBarTrailing) {
                    Button("设置") { }
                }
            }
        }
    }
}

struct DetailView: View {
    let title: String
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        VStack {
            Text("这是 \(title) 页面")
                .font(.largeTitle)
            Button("返回") {
                dismiss()
            }
        }
    }
}

5. 动画系统

swift 复制代码
struct AnimationDemoView: View {
    @State private var isExpanded = false
    @State private var rotation = 0.0
    
    var body: some View {
        VStack(spacing: 30) {
            // 隐式动画
            RoundedRectangle(cornerRadius: isExpanded ? 50 : 10)
                .fill(Color.blue)
                .frame(width: isExpanded ? 200 : 100,
                       height: isExpanded ? 200 : 100)
                .animation(.easeInOut(duration: 0.5), value: isExpanded)
            
            // 显式动画
            Image(systemName: "arrow.clockwise")
                .font(.largeTitle)
                .rotationEffect(.degrees(rotation))
                .onTapGesture {
                    withAnimation(.spring(response: 0.6, dampingFraction: 0.8)) {
                        rotation += 360
                    }
                }
            
            // 过渡动画
            if isExpanded {
                Text("出现/消失动画")
                    .transition(.scale.combined(with: .opacity))
            }
            
            Button("切换动画") {
                withAnimation {
                    isExpanded.toggle()
                }
            }
            .buttonStyle(.borderedProminent)
        }
        .padding()
    }
}

6. 与 UIKit 混合使用

swift 复制代码
// 在 SwiftUI 中嵌入 UIKit 组件
struct MapView: UIViewRepresentable {
    let coordinate: CLLocationCoordinate2D
    
    func makeUIView(context: Context) -> MKMapView {
        MKMapView(frame: .zero)
    }
    
    func updateUIView(_ uiView: MKMapView, context: Context) {
        let span = MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02)
        let region = MKCoordinateRegion(center: coordinate, span: span)
        uiView.setRegion(region, animated: true)
    }
}

// 在 UIKit 中嵌入 SwiftUI 视图
class HostingViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let swiftUIView = Text("来自 SwiftUI")
            .font(.largeTitle)
            .padding()
        
        let hostingController = UIHostingController(rootView: swiftUIView)
        addChild(hostingController)
        view.addSubview(hostingController.view)
        hostingController.didMove(toParent: self)
        
        hostingController.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            hostingController.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            hostingController.view.centerYAnchor.constraint(equalTo: view.centerYAnchor)
        ])
    }
}

📊 对比分析

1. 代码量对比

相同功能登录页面:

框架 代码行数 Builder 文件 总复杂度
UIKit + Storyboard ~60 行 + Storyboard 1 个 XML 中等
UIKit + 纯代码 ~120 行 1 个 Swift 较高
SwiftUI ~30 行 1 个 Swift

2. 性能对比

场景 UIKit SwiftUI 说明
列表滚动 (1000 行) ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ SwiftUI 需要 Lazy 容器
复杂动画 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ 复杂动画需用 UIKit 兜底
启动速度 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ SwiftUI 首次渲染稍慢
内存占用 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ SwiftUI 状态管理有开销
渲染帧率 60/120fps 60/120fps 两者可达流畅体验

3. 使用量统计(全球 iOS 应用)

指标 UIKit SwiftUI
存量项目占比 ~85% ~15%
2024 新项目占比 ~40% ~60%
大型企业项目 ~70% ~30%
个人/小团队项目 ~30% ~70%

4. 学习曲线

text 复制代码
学习难度
    ↑
高   │  UIKit纯代码 ──────────┐
    │                        │
中   │  UIKit+SB              SwiftUI
    │                        │
低   │                       ↓
    └──────────────────────────────→ 时间
         1周      1月      3月      1年

🎯 选型建议

选择 UIKit 的场景

markdown 复制代码
✅ 适合 UIKit 的情况:
1. 维护 3 年以上的老项目
2. App 需要支持 iOS 13 以下版本
3. 团队 UIKit 经验丰富,SwiftUI 经验为 0
4. 应用有大量自定义复杂动画
5. 使用大量未适配 SwiftUI 的第三方库
6. 超长列表(如聊天记录、相册)需要极致性能
7. 深度集成 Core Graphics/Core Animation
8. 使用 SceneKit/ARKit 等 3D 框架

选择 SwiftUI 的场景

markdown 复制代码
✅ 适合 SwiftUI 的情况:
1. 全新项目,无历史包袱
2. App 最低支持 iOS 15+(最佳体验 iOS 17+)
3. 团队有声明式 UI 经验(React/Flutter/Compose)
4. 快速原型开发或 MVP 产品
5. 跨 Apple 平台应用(iOS + macOS + watchOS)
6. UI 以标准组件为主,定制程度低
7. 希望减少代码量,提高开发效率
8. 需要实时预览快速迭代

混合开发策略(推荐)

swift 复制代码
// 最佳实践:渐进式迁移
// 第一阶段:新页面用 SwiftUI
struct NewSettingsView: View {
    var body: some View {
        // SwiftUI 实现
    }
}

// 在 UIKit 中展示
let hostingVC = UIHostingController(rootView: NewSettingsView())
navigationController?.pushViewController(hostingVC, animated: true)

// 第二阶段:复杂组件保留 UIKit
struct CustomMapView: UIViewRepresentable {
    // 高性能地图组件继续用 UIKit
}

// 第三阶段:逐步将老页面重写

📝 总结

UIKit 核心要点

  • 成熟稳定:10+ 年验证,坑都已踩平
  • 高性能:列表滚动、动画极致优化
  • 生态丰富:第三方库最多
  • 代码量大:需要编写大量模板代码
  • 状态管理复杂:容易出 Bug
  • 未来发展有限:Apple 不再加入新特性

SwiftUI 核心要点

  • 代码简洁:减少 50-70% 代码量
  • 状态驱动:自动更新 UI,减少 Bug
  • 跨平台:一套代码多端运行
  • Apple 主推:未来所有新特性只在 SwiftUI 发布
  • 需 iOS 13+:无法支持老设备
  • 部分场景未成熟:复杂动画、长列表仍有坑
  • 生态积累不足:部分三方库未适配

最终建议

对于 2024 年及以后的新项目,优先选择 SwiftUI。遇到 SwiftUI 无法满足的需求时,通过 UIViewRepresentableUIViewControllerRepresentable 嵌入 UIKit 组件。这种混合模式既能享受 SwiftUI 的开发效率,又能充分利用 UIKit 的成熟生态。


📚 参考资源


文档版本:1.0
最后更新:2026 年 5 月

相关推荐
MonkeyKing3 小时前
iOS 循环引用深度解析:delegate/block/NSTimer/嵌套闭包
ios
泉木3 小时前
KVO 详解 —— iOS/ObjC 完整学习指南
ios·objective-c
MonkeyKing3 小时前
iOS AutoreleasePool 深度解析:原理、Page结构与释放时机
ios
报错小能手3 小时前
Swift经典面试题汇总
开发语言·ios·swift
迷途酱3 小时前
Swift 真的被搞得乱七八糟了吗?写了几年之后说点实话
ios·swift
莫生灬灬3 小时前
ElementUI封装 共91个组件 支持易语言/火山/C#/Python
开发语言·c++·python·ui·elementui·c#
唐诺4 小时前
iOS UI 框架详解
ui·ios
Zender Han4 小时前
Flutter 轻量存储方案介绍、区别、对比和使用场景
android·flutter·ios
2501_916007474 小时前
XCode 15 IDE新特性:苹果集成开发环境全面升级,提升编程效率与体验
ide·vscode·macos·ios·个人开发·xcode·敏捷流程