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 月

相关推荐
for_ever_love__2 小时前
UI学习:UISearchController基础了解和应用
学习·ui·ios·objective-c
ZC跨境爬虫3 小时前
跟着 MDN 学CSS day_39:(Flexbox 弹性盒子核心机制)
前端·css·ui·html·tensorflow
代码的小搬运工6 小时前
ZARA仿写
ios
海兰7 小时前
【文字三国志:第六篇】天命重构,UI组件设计细节
人工智能·ui·语言模型·小程序
人月神话Lee8 小时前
【图像处理】vImage/Accelerate——SIMD 让 CPU 也能飞
ios·swift·图像识别
EMTime9 小时前
玲珑GUI-工程设置
单片机·mcu·ui·用户界面
小拉达不是臭老鼠11 小时前
Unity中的UI系统之UGUI
学习·ui·unity
2601_9557674212 小时前
iPhone 17 护眼钢化膜怎么选?从PWM频闪到圆偏振光,解析「软硬协同」光学方案
ios·ar·iphone·护眼钢化膜·圆偏振光·#观复盾护景贴·磁控溅射
2601_9557674215 小时前
iPhone 17 护眼保护膜怎么选?圆偏振光 + AR 抗眩方案,解读 96% 透光率与 ≤0.5% 反射率的协同价值
ios·ar·iphone·圆偏振光·#观复盾护景贴·scinique双护技术
三雒15 小时前
KMP 实战:Android 开发如何快速统一双端 IM 模块
android·ios·kotlin