目录
SwiftUI 是 Apple 在 2019 年推出的革命性 UI 框架,采用声明式语法构建 Apple 全平台应用。下面我将全面介绍 SwiftUI 的核心概念并提供实用示例。
一、SwiftUI 核心优势
特点 | 说明 |
---|---|
声明式语法 | 描述 UI 应该是什么样子,而不是如何一步步创建 |
实时预览 | Xcode 中实时查看 UI 变化,无需编译运行 |
跨平台支持 | iOS, macOS, watchOS, tvOS 统一框架 |
状态驱动 | 自动响应数据变化更新 UI |
组合式组件 | 通过简单组件组合构建复杂界面 |
动画简单化 | 内置丰富的动画效果,一行代码实现 |
二、基础组件与布局
2.1、基本视图组件
import SwiftUI
struct BasicComponentsView: View {
var body: some View {
VStack(spacing: 20) {
// 文本
Text("Hello SwiftUI!")
.font(.largeTitle)
.foregroundStyle(.blue)
// 按钮
Button("点击我") {
print("按钮被点击!")
}
.buttonStyle(.borderedProminent)
.tint(.purple)
// 图片
Image(systemName: "heart.fill")
.resizable()
.frame(width: 60, height: 60)
.foregroundStyle(.red)
// 文本输入框
TextField("请输入内容", text: .constant(""))
.textFieldStyle(.roundedBorder)
.padding()
}
.padding()
}
}
2.2、布局系统
struct LayoutSystemView: View {
var body: some View {
HStack(spacing: 20) {
// 垂直布局
VStack {
RoundedRectangle(cornerRadius: 15)
.fill(.red)
.frame(width: 80, height: 120)
Text("VStack")
}
// 水平布局
HStack {
RoundedRectangle(cornerRadius: 15)
.fill(.green)
.frame(width: 120, height: 80)
Text("HStack")
}
// 层叠布局
ZStack {
RoundedRectangle(cornerRadius: 15)
.fill(.blue)
.frame(width: 100, height: 100)
Text("ZStack")
.foregroundStyle(.white)
}
}
.padding()
}
}
2.3、列表与导航
struct ListItem: Identifiable {
let id = UUID()
let name: String
let icon: String
}
struct ListNavigationView: View {
let items = [
ListItem(name: "设置", icon: "gear"),
ListItem(name: "收藏", icon: "star.fill"),
ListItem(name: "历史记录", icon: "clock.fill"),
ListItem(name: "帮助", icon: "questionmark.circle")
]
var body: some View {
NavigationStack {
List(items) { item in
NavigationLink {
// 详情视图
VStack {
Image(systemName: item.icon)
.resizable()
.scaledToFit()
.frame(width: 100, height: 100)
.foregroundStyle(.blue)
Text(item.name)
.font(.title)
}
.navigationTitle(item.name)
} label: {
HStack {
Image(systemName: item.icon)
.foregroundStyle(.orange)
Text(item.name)
}
}
}
.navigationTitle("功能列表")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button(action: {}) {
Image(systemName: "plus")
}
}
}
}
}
}
三、状态管理与数据流
3.1、状态管理基础
struct StateManagementView: View {
// 简单状态
@State private var counter = 0
@State private var isToggled = false
var body: some View {
VStack(spacing: 30) {
// 计数器
Text("计数: \(counter)")
.font(.title)
HStack(spacing: 20) {
Button("减少") { counter -= 1 }
.buttonStyle(.bordered)
Button("增加") { counter += 1 }
.buttonStyle(.borderedProminent)
}
// 开关
Toggle("开关状态", isOn: $isToggled)
.padding()
.background(isToggled ? .green.opacity(0.2) : .gray.opacity(0.1))
.cornerRadius(10)
// 根据状态显示不同内容
if isToggled {
Text("开关已开启!")
.foregroundStyle(.green)
.transition(.opacity)
}
}
.padding()
.animation(.easeInOut, value: isToggled)
}
}
3.2、数据绑定与共享
// 可观察对象
class UserSettings: ObservableObject {
@Published var username = "用户"
@Published var isDarkMode = false
}
struct DataBindingView: View {
@StateObject private var settings = UserSettings()
@State private var newUsername = ""
var body: some View {
VStack(spacing: 30) {
// 显示共享数据
Text("欢迎, \(settings.username)!")
.font(.title)
// 修改共享数据
HStack {
TextField("新用户名", text: $newUsername)
.textFieldStyle(.roundedBorder)
Button("更新") {
settings.username = newUsername
newUsername = ""
}
.disabled(newUsername.isEmpty)
}
// 绑定到共享属性
Toggle("深色模式", isOn: $settings.isDarkMode)
.padding()
.background(settings.isDarkMode ? .black.opacity(0.3) : .white)
.cornerRadius(10)
// 子视图共享数据
SettingsPreview(settings: settings)
}
.padding()
.environmentObject(settings)
}
}
// 子视图
struct SettingsPreview: View {
@ObservedObject var settings: UserSettings
var body: some View {
VStack {
Text("预览")
.font(.headline)
Text("用户名: \(settings.username)")
Text("模式: \(settings.isDarkMode ? "深色" : "浅色")")
}
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(10)
}
}
四、高级功能与技巧
4.1、动画效果
struct AnimationsView: View {
@State private var isAnimating = false
@State private var rotation: Double = 0
var body: some View {
VStack(spacing: 50) {
// 简单动画
RoundedRectangle(cornerRadius: isAnimating ? 50 : 10)
.fill(isAnimating ? .orange : .blue)
.frame(
width: isAnimating ? 200 : 100,
height: isAnimating ? 200 : 100
)
.rotationEffect(.degrees(rotation))
.animation(.spring(response: 0.5, dampingFraction: 0.3), value: isAnimating)
// 显式动画
Button("旋转") {
withAnimation(.easeInOut(duration: 1)) {
rotation += 360
}
}
// 切换动画状态
Button(isAnimating ? "重置" : "动画开始") {
isAnimating.toggle()
}
.buttonStyle(.borderedProminent)
}
}
}
4.2、绘图与自定义形状
struct DrawingView: View {
var body: some View {
VStack {
// 基本形状
HStack(spacing: 20) {
Circle()
.fill(LinearGradient(
gradient: Gradient(colors: [.blue, .purple]),
startPoint: .top,
endPoint: .bottom
))
.frame(width: 80, height: 80)
Capsule()
.fill(AngularGradient(
gradient: Gradient(colors: [.red, .yellow, .green, .blue, .purple, .red]),
center: .center
))
.frame(width: 60, height: 100)
}
// 自定义路径
Path { path in
path.move(to: CGPoint(x: 50, y: 0))
path.addLine(to: CGPoint(x: 100, y: 50))
path.addLine(to: CGPoint(x: 50, y: 100))
path.addLine(to: CGPoint(x: 0, y: 50))
path.closeSubpath()
}
.fill(LinearGradient(
gradient: Gradient(colors: [.green, .mint]),
startPoint: .topLeading,
endPoint: .bottomTrailing
))
.frame(width: 100, height: 100)
.padding()
// 自定义形状
StarShape(points: 5, innerRatio: 0.45)
.fill(RadialGradient(
gradient: Gradient(colors: [.yellow, .orange]),
center: .center,
startRadius: 0,
endRadius: 50
))
.frame(width: 150, height: 150)
}
}
}
// 自定义星形形状
struct StarShape: Shape {
let points: Int
let innerRatio: CGFloat
func path(in rect: CGRect) -> Path {
Path { path in
let center = CGPoint(x: rect.width / 2, y: rect.height / 2)
let outerRadius = min(rect.width, rect.height) / 2
let innerRadius = outerRadius * innerRatio
let anglePerPoint = .pi * 2 / CGFloat(points)
let offset = -CGFloat.pi / 2
for i in 0..<points * 2 {
let angle = offset + anglePerPoint * CGFloat(i)
let radius = i.isMultiple(of: 2) ? outerRadius : innerRadius
let point = CGPoint(
x: center.x + radius * cos(angle),
y: center.y + radius * sin(angle)
)
if i == 0 {
path.move(to: point)
} else {
path.addLine(to: point)
}
}
path.closeSubpath()
}
}
}
4.3、网络请求与异步数据
struct Post: Codable, Identifiable {
let id: Int
let title: String
let body: String
}
struct NetworkExampleView: View {
@State private var posts: [Post] = []
@State private var isLoading = false
@State private var errorMessage: String?
var body: some View {
Group {
if isLoading {
ProgressView("加载中...")
.scaleEffect(1.5)
} else if let error = errorMessage {
VStack {
Image(systemName: "exclamationmark.triangle")
.font(.largeTitle)
Text(error)
}
.foregroundStyle(.red)
} else {
List(posts) { post in
VStack(alignment: .leading) {
Text(post.title)
.font(.headline)
Text(post.body)
.font(.subheadline)
.foregroundStyle(.secondary)
.lineLimit(2)
}
}
.refreshable {
await fetchPosts()
}
}
}
.navigationTitle("网络请求示例")
.task {
await fetchPosts()
}
}
func fetchPosts() async {
isLoading = true
errorMessage = nil
do {
let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
let (data, _) = try await URLSession.shared.data(from: url)
posts = try JSONDecoder().decode([Post].self, from: data)
} catch {
errorMessage = "加载失败: \(error.localizedDescription)"
}
isLoading = false
}
}
五、SwiftUI 最佳实践
- 组件化设计:将UI拆分为小型可复用组件
- 预览驱动开发:充分利用Xcode预览功能
- 状态最小化:仅使必要的状态可变
- 使用环境对象:跨视图层级共享数据
- 性能优化:
- 对列表项使用
Identifiable
协议 - 避免在视图主体中执行繁重操作
- 使用
EquatableView
优化视图更新
- 对列表项使用
平台适配:
#if os(iOS)
// iOS特定代码
#elseif os(macOS)
// macOS特定代码
#endif
六、SwiftUI 开发环境要求
平台 | 最低版本要求 |
---|---|
iOS | 13.0+ |
macOS | 10.15+ |
watchOS | 6.0+ |
tvOS | 13.0+ |
Xcode | 11.0+ |
SwiftUI 代表了 Apple 开发生态的未来方向,通过声明式语法和响应式设计大大提高了开发效率。虽然学习曲线存在,但其强大的功能和开发体验使其成为构建 Apple 平台应用的首选框架。