📖 学习内容
SwiftUI 采用 数据驱动 UI,主要使用 @State, @Binding, @ObservedObject, @EnvironmentObject 来管理数据状态。
1️⃣ @State(管理本地状态)
@State 用于组件内的小范围状态管理,比如计数器:
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Count: (count)")
Button("Increase") {
count += 1
}
}
}
}
-
@State 修饰的变量只适用于当前 View,每次修改都会触发 UI 更新。
2️⃣ @Binding(父子组件数据传递)
用于在 父子组件之间共享状态,如 Toggle 控制开关:
struct ParentView: View {
@State private var isOn = false
var body: some View {
ToggleView(isOn: $isOn)
}
}
struct ToggleView: View {
@Binding var isOn: Bool
var body: some View {
Toggle("开关", isOn: $isOn)
}
}
-
$isOn 传递 @State 变量,使子组件能修改父组件的值。
3️⃣ @ObservedObject & @StateObject(可观察对象)
适用于 多个视图共享数据。
class TaskManager: ObservableObject {
@Published var tasks = ["学习 SwiftUI", "写博客"]
}
struct TaskListView: View {
@StateObject private var manager = TaskManager()
var body: some View {
List(manager.tasks, id: .self) { task in
Text(task)
}
}
}
-
@Published 让 tasks 变化时 UI 自动更新。
-
@StateObject 在 视图创建时初始化对象。
4️⃣ @EnvironmentObject(全局状态)
适用于 多个视图共享数据,如 UserSettings:
class UserSettings: ObservableObject {
@Published var username = "SwiftUI 学习者"
}
struct ContentView: View {
@StateObject private var settings = UserSettings()
var body: some View {
UserProfileView().environmentObject(settings)
}
}
struct UserProfileView: View {
@EnvironmentObject var settings: UserSettings
var body: some View {
Text("用户名: (settings.username)")
}
}
-
@EnvironmentObject 让 settings 适用于整个 app。
5️⃣ List 和 ForEach(列表视图)
SwiftUI 的 List 适用于创建可滚动的列表:
struct TodoListView: View {
let tasks = ["买菜", "写代码", "运动"]
var body: some View {
List(tasks, id: .self) { task in
Text(task)
}
}
}
-
List 会自动处理滚动和复用,比 VStack 更高效。
6️⃣ NavigationStack(页面跳转)
用于 在 SwiftUI 里导航:
struct HomeView: View {
var body: some View {
NavigationStack {
NavigationLink("去详情页", destination: DetailView())
}
}
}
struct DetailView: View {
var body: some View {
Text("详情页")
}
}
- NavigationLink 允许用户点击后跳转。
💻 练习项目
✅ To-Do List 应用****
-
List 显示任务,@State 记录完成状态
-
TextField 输入新任务
✅ 新闻列表 UI****
- List + NavigationStack 实现新闻列表
- 点击新闻后进入 详情页****
- 练习 LazyVStack 实现懒加载
✅练习 1:To-Do List 应用
实现一个可添加、标记完成状态的代办事项列表。
import SwiftUI
struct TodoItem: Identifiable {
let id = UUID()
var title: String
var isCompleted: Bool = false
}
class TodoViewModel: ObservableObject {
@Published var todos: [TodoItem] = []
func addTodo(_ title: String) {
let newTodo = TodoItem(title: title)
todos.append(newTodo)
}
func toggleTodo(_ todo: TodoItem) {
if let index = todos.firstIndex(where: { $0.id == todo.id }) {
todos[index].isCompleted.toggle()
}
}
func deleteTodo(at offsets: IndexSet) {
todos.remove(atOffsets: offsets)
}
}
struct TodoListView: View {
@StateObject private var viewModel = TodoViewModel()
@State private var newTodo = ""
var body: some View {
NavigationStack {
VStack {
HStack {
TextField("输入新任务", text: $newTodo)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button("添加") {
guard !newTodo.isEmpty else { return }
viewModel.addTodo(newTodo)
newTodo = ""
}
.buttonStyle(.borderedProminent)
}
.padding()
List {
ForEach(viewModel.todos) { todo in
HStack {
Image(systemName: todo.isCompleted ? "checkmark.circle.fill" : "circle")
.foregroundColor(todo.isCompleted ? .green : .gray)
Text(todo.title)
.strikethrough(todo.isCompleted)
.foregroundColor(todo.isCompleted ? .gray : .primary)
}
.onTapGesture {
viewModel.toggleTodo(todo)
}
}
.onDelete(perform: viewModel.deleteTodo)
}
}
.navigationTitle("待办事项")
}
}
}
#Preview {
TodoListView()
}
✅练习 2:新闻列表 UI(含导航跳转)
import SwiftUI
struct NewsArticle: Identifiable {
let id = UUID()
let title: String
let content: String
}
struct NewsListView: View {
let articles: [NewsArticle] = [
NewsArticle(title: "SwiftUI 入门", content: "这是关于 SwiftUI 的入门教程..."),
NewsArticle(title: "iOS 17 新特性", content: "探索 iOS 17 中的新功能..."),
NewsArticle(title: "Apple Vision Pro 上手", content: "Vision Pro 正在改变一切...")
]
var body: some View {
NavigationStack {
List(articles) { article in
NavigationLink(destination: NewsDetailView(article: article)) {
Text(article.title)
.font(.headline)
.padding(.vertical, 4)
}
}
.navigationTitle("新闻列表")
}
}
}
struct NewsDetailView: View {
let article: NewsArticle
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 12) {
Text(article.title)
.font(.title)
.bold()
Text(article.content)
.font(.body)
}
.padding()
}
.navigationTitle("详情")
}
}
#Preview {
NewsListView()
}
📌 这两个练习会帮助我们掌握 SwiftUI 中的状态绑定、列表渲染、以及基本导航逻辑。