SwiftUI 和 UIKit 是苹果提供的两个 UI 框架,它们在设计理念、开发方式和能力上有显著区别:
- 设计理念
SwiftUI - 声明式
swift
struct ContentView: View {
@State private var isOn = false
var body: some View {
VStack {
Toggle("开关", isOn: $isOn)
if isOn {
Text("开关已打开")
.foregroundColor(.green)
}
}
.padding()
}
}
UIKit - 命令式
swift
class ViewController: UIViewController {
private let toggle = UISwitch()
private let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
toggle.addTarget(self, action: #selector(toggleChanged), for: .valueChanged)
}
@objc func toggleChanged() {
label.isHidden = !toggle.isOn
label.text = "开关已打开"
label.textColor = .green
}
private func setupUI() {
// 手动创建和配置所有视图
}
}
- 数据绑定和状态管理
SwiftUI - 自动响应
swift
struct UserProfile: View {
@State private var username = ""
@StateObject private var viewModel = ProfileViewModel()
var body: some View {
VStack {
TextField("用户名", text: $username)
Text("你好, \(username)")
.opacity(username.isEmpty ? 0 : 1)
Button("保存") {
viewModel.saveUser(name: username)
}
}
}
}
UIKit - 手动更新
swift
class UserProfileViewController: UIViewController {
@IBOutlet weak var usernameField: UITextField!
@IBOutlet weak var greetingLabel: UILabel!
@IBOutlet weak var saveButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
usernameField.addTarget(self, action: #selector(textFieldChanged), for: .editingChanged)
}
@objc func textFieldChanged() {
let username = usernameField.text ?? ""
greetingLabel.text = "你好, \(username)"
greetingLabel.isHidden = username.isEmpty
saveButton.isEnabled = !username.isEmpty
}
}
- 布局系统
SwiftUI - 修饰符布局
swift
VStack(spacing: 20) {
Text("标题")
.font(.title)
.padding()
HStack {
Image(systemName: "star")
Text("评分: 4.8")
}
Spacer()
Button("确认") { }
.frame(maxWidth: .infinity)
.padding()
}
.background(Color.gray.opacity(0.1))
UIKit - 自动布局约束
swift
private func setupConstraints() {
NSLayoutConstraint.activate([
titleLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 20),
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
confirmButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -20),
confirmButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
confirmButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
confirmButton.heightAnchor.constraint(equalToConstant: 50)
])
}
- 视图生命周期
SwiftUI - 基于视图结构
swift
struct LifecycleView: View {
var body: some View {
Text("示例")
}
// 生命周期事件
.onAppear {
print("视图出现")
}
.onDisappear {
print("视图消失")
}
.task {
// 异步任务,自动取消
await loadData()
}
}
UIKit - 基于视图控制器
swift
class LifecycleViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 视图加载完成
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// 视图将要出现
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// 视图已经出现
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// 视图将要消失
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// 视图已经消失
}
}
- 列表和集合视图
SwiftUI - List 和 ForEach
swift
struct UserListView: View {
let users: [User]
var body: some View {
List(users) { user in
HStack {
AsyncImage(url: user.avatarURL) { image in
image.resizable()
} placeholder: {
ProgressView()
}
.frame(width: 50, height: 50)
.clipShape(Circle())
VStack(alignment: .leading) {
Text(user.name)
.font(.headline)
Text(user.email)
.font(.subheadline)
.foregroundColor(.gray)
}
}
.swipeActions {
Button("删除", role: .destructive) {
deleteUser(user)
}
}
}
.refreshable {
await refreshUsers()
}
}
}
UIKit - UITableView
swift
class UserListViewController: UITableViewController {
var users: [User] = []
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell", for: indexPath) as! UserCell
let user = users[indexPath.row]
cell.configure(with: user)
return cell
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
deleteUser(at: indexPath)
}
}
}
- 动画和过渡
SwiftUI - 隐式动画
swift
struct AnimatedView: View {
@State private var isExpanded = false
var body: some View {
VStack {
Button("切换") {
withAnimation(.spring()) {
isExpanded.toggle()
}
}
Rectangle()
.fill(Color.blue)
.frame(
width: isExpanded ? 200 : 100,
height: isExpanded ? 200 : 100
)
.rotationEffect(.degrees(isExpanded ? 180 : 0))
}
}
}
UIKit - 显式动画
swift
class AnimatedViewController: UIViewController {
@IBOutlet weak var boxView: UIView!
var isExpanded = false
@IBAction func toggleTapped(_ sender: UIButton) {
UIView.animate(withDuration: 0.5,
delay: 0,
usingSpringWithDamping: 0.6,
initialSpringVelocity: 0,
options: []) {
if self.isExpanded {
self.boxView.transform = CGAffineTransform.identity
} else {
self.boxView.transform = CGAffineTransform(rotationAngle: .pi)
.scaledBy(x: 2, y: 2)
}
} completion: { _ in
self.isExpanded.toggle()
}
}
}
- 导航和路由
SwiftUI - NavigationStack
swift
struct MainApp: View {
var body: some View {
NavigationStack {
List {
NavigationLink("用户详情", value: "user_detail")
NavigationLink("设置", value: "settings")
}
.navigationDestination(for: String.self) { route in
switch route {
case "user_detail":
UserDetailView()
case "settings":
SettingsView()
default:
Text("未知页面")
}
}
}
}
}
UIKit - UINavigationController
swift
class MainViewController: UITableViewController {
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.row {
case 0:
let userDetailVC = UserDetailViewController()
navigationController?.pushViewController(userDetailVC, animated: true)
case 1:
let settingsVC = SettingsViewController()
navigationController?.pushViewController(settingsVC, animated: true)
default:
break
}
}
}
- 主要区别总结
特性 SwiftUI UIKit 编程范式 声明式 命令式 数据流 响应式数据绑定 手动更新 布局 基于栈的布局系统 Auto Layout 约束 状态管理 @State, @Binding, @ObservedObject 手动管理状态 预览 实时预览 Interface Builder 跨平台 iOS, macOS, watchOS, tvOS 主要 iOS/tvOS 学习曲线 相对平缓 相对陡峭 代码量 通常更少 通常更多 版本要求 iOS 13+ iOS 2+ 性能 优化良好,持续改进 成熟稳定
- 混合使用
在实际项目中,可以混合使用两者:
swift
// 在 SwiftUI 中使用 UIKit 组件
struct MapView: UIViewRepresentable {
func makeUIView(context: Context) -> MKMapView {
MKMapView()
}
func updateUIView(_ uiView: MKMapView, context: Context) {
// 更新地图
}
}
// 在 UIKit 中使用 SwiftUI
class HostingViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let swiftUIView = ContentView()
let hostingController = UIHostingController(rootView: swiftUIView)
addChild(hostingController)
view.addSubview(hostingController.view)
hostingController.didMove(toParent: self)
}
}