SwiftUI中常用的属性包装器

26 阅读5分钟

一、属性包装器(Property Wrappers)

用于管理数据流、状态和与视图的交互。

1. @State

  • 作用:声明视图内部的状态,仅用于视图私有的简单数据。
  • 特点:当值变化时,视图自动更新。
  • 示例
    struct CounterView: View {
        @State private var count = 0
        var body: some View {
            Button("点击次数: \(count)") { count += 1 }
        }
    }
    

2. @Binding

  • 作用:创建对父视图状态的引用,实现双向数据绑定。
  • 示例
    struct ChildView: View {
        @Binding var isOn: Bool
        var body: some View {
            Toggle("开关", isOn: $isOn)
        }
    }
    
    struct ParentView: View {
        @State private var isOn = false
        var body: some View {
            ChildView(isOn: $isOn)
        }
    }
    

3. @ObservedObject

  • 作用:观察外部符合 ObservableObject 协议的类对象,用于复杂数据模型。
  • 示例
    class DataModel: ObservableObject {
        @Published var username = "User"
    }
    
    struct ProfileView: View {
        @ObservedObject var model: DataModel
        var body: some View {
            Text("用户名: \(model.username)")
        }
    }
    

4. @StateObject

  • 作用:与 @ObservedObject 类似,但由视图持有其生命周期,避免重复创建。
  • 示例
    struct ContentView: View {
        @StateObject private var model = DataModel()
        var body: some View {
            ProfileView(model: model)
        }
    }
    

5. @EnvironmentObject

  • 作用:从环境中获取共享的全局对象,避免逐层传递。
  • 示例
    class Settings: ObservableObject {
        @Published var themeColor = Color.blue
    }
    
    struct ThemeView: View {
        @EnvironmentObject var settings: Settings
        var body: some View {
            Text("主题颜色").foregroundColor(settings.themeColor)
        }
    }
    
    // 在父视图中注入
    ContentView().environmentObject(Settings())
    

6. @Environment

  • 作用:读取系统或自定义环境值(如暗黑模式、布局方向等)。
  • 示例
    struct DarkModeView: View {
        @Environment(\.colorScheme) var colorScheme
        var body: some View {
            Text(colorScheme == .dark ? "暗黑模式" : "明亮模式")
        }
    }
    

7. @FetchRequest (Core Data)

  • 作用:自动从 Core Data 中获取数据。
  • 示例
    struct TaskListView: View {
        @FetchRequest(
            entity: Task.entity(),
            sortDescriptors: [NSSortDescriptor(keyPath: \Task.date, ascending: true)]
        ) var tasks: FetchedResults<Task>
        
        var body: some View {
            List(tasks) { task in
                Text(task.title ?? "")
            }
        }
    }
    

8. @AppStorage / @SceneStorage

  • 作用:轻量级数据持久化(基于 UserDefaults 或场景存储)。
  • 示例
    struct SettingsView: View {
        @AppStorage("isDarkMode") private var isDarkMode = false
        var body: some View {
            Toggle("暗黑模式", isOn: $isDarkMode)
        }
    }
    

二、关键视图修饰符(View Modifiers)

用于调整视图的布局、样式或行为。

1. 布局修饰符

  • .padding():添加内边距。
    Text("Hello").padding(10)
    
  • .frame():设置视图尺寸。
    Text("Hello").frame(width: 100, height: 50)
    

2. 样式修饰符

  • .foregroundColor() / .background()
    Text("Hello").foregroundColor(.red).background(Color.yellow)
    
  • .font()
    Text("Hello").font(.title.bold())
    

3. 交互修饰符

  • .onTapGesture:点击手势。
    Text("点击我").onTapGesture { print("点击") }
    
  • .onAppear / .onDisappear:生命周期回调。
    Text("Hello").onAppear { print("视图出现") }
    

4. 动画修饰符

  • .animation():添加动画效果。
    Button("切换") { isOn.toggle() }
      .animation(.easeInOut, value: isOn)
    

四、属性包装器(Property Wrappers)总结

1. 数据管理

属性包装器作用示例代码
@State管理视图内部私有状态,值变化触发视图更新。@State private var isEnabled = true
@Binding建立父子视图间的双向数据绑定。@Binding var text: String → 父视图传递 $text
@StateObject持有符合 ObservableObject 的对象,生命周期与视图一致。@StateObject var viewModel = UserViewModel()
@ObservedObject观察外部 ObservableObject 对象,不持有所有权。@ObservedObject var dataModel: DataModel
@EnvironmentObject从环境全局共享对象,避免逐层传递。@EnvironmentObject var settings: AppSettings
@Environment读取系统或自定义环境值(如设备特征)。@Environment(.horizontalSizeClass) var sizeClass
@AppStorage轻量级持久化存储,基于 UserDefaults@AppStorage("username") var name: String = "Anonymous"
@SceneStorage场景级存储,用于多窗口应用的状态恢复。@SceneStorage("selectedTab") var tab: Int = 0
@FocusedValue监听当前焦点所在视图的值(如 TextField 聚焦时)。@FocusedValue(.username) var currentUsername
@GestureState跟踪手势交互的临时状态(如拖拽偏移量)。@GestureState var dragOffset = CGSize.zero

2. 数据流与模型

属性包装器作用示例代码
@Published标记 ObservableObject 中需要发布变化的属性。class User: ObservableObject { @Published var name = "John" }
@FetchRequest自动从 Core Data 中获取数据(需导入 CoreData)。@FetchRequest(sortDescriptors: []) var items: FetchedResults<Item>
@Query (SwiftData)SwiftData 中的数据查询(iOS 17+)。@Query var users: [User]

3. UI 交互

属性包装器作用示例代码
@FocusState控制 TextField 等组件的焦点状态。@FocusState private var isEmailFocused: Bool
@Namespace创建动画命名空间,用于匹配视图间的动画过渡。@Namespace private var animationNamespace

五、视图修饰符(View Modifiers)

1. 布局与尺寸

修饰符作用示例代码
.frame(width:height:)设置视图尺寸,可指定对齐方式。.frame(width: 200, height: 100, alignment: .center)
.padding(_:)添加内边距。.padding(.horizontal, 20)
.offset(x:y:)偏移视图位置。.offset(x: 10, y: -5)
.position(x:y:)绝对定位视图中心点(相对于父视图坐标)。.position(x: 100, y: 200)
.aspectRatio(_:contentMode:)控制视图宽高比。.aspectRatio(16/9, contentMode: .fit)

2. 样式与外观

修饰符作用示例代码
.foregroundColor(_:)设置前景色(文本、图标颜色)。.foregroundColor(.blue)
.background(_:alignment:)设置背景视图或颜色。.background(Color.yellow.opacity(0.3))
.cornerRadius(_:)添加圆角(iOS 15+ 推荐使用 .clipShape 更灵活)。.cornerRadius(10)
.shadow(color:radius:x:y:)添加阴影。.shadow(color: .gray, radius: 5, x: 2, y: 2)
.blur(radius:opaque:)添加模糊效果。.blur(radius: 3)
.opacity(_:)设置透明度(0~1)。.opacity(0.5)

3. 文本与字体

修饰符作用示例代码
.font(_:)设置字体样式。.font(.system(size: 16, weight: .bold, design: .rounded))
.lineLimit(_:)限制文本行数。.lineLimit(3)
.multilineTextAlignment(_:)多行文本对齐方式。.multilineTextAlignment(.trailing)
.textFieldStyle(_:)设置 TextField 样式(如 .roundedBorder)。.textFieldStyle(.roundedBorder)

4. 交互与事件

修饰符作用示例代码
.onTapGesture(count:perform:)添加点击手势。.onTapGesture(count: 2) { print("双击") }
.onLongPressGesture(minimumDuration:perform:)长按手势。.onLongPressGesture(minimumDuration: 1) { print("长按") }
.gesture(_:)添加自定义手势(如拖拽、旋转)。.gesture(DragGesture().onChanged { value in ... })
.onSubmit(of:_:)响应键盘提交事件(如 TextField 回车)。.onSubmit { print("提交") }
.disabled(_:)禁用视图交互。.disabled(!isFormValid)

5. 动画与过渡

修饰符作用示例代码
.animation(_:value:)为特定值变化添加动画。.animation(.spring(), value: isExpanded)
.transition(_:)定义视图插入或移除的过渡效果。.transition(.asymmetric(insertion: .scale, removal: .opacity))
.matchedGeometryEffect(id:in:)关联两个视图的几何属性,用于平滑动画。.matchedGeometryEffect(id: "circle", in: animationNamespace)

6. 列表与滚动视图

修饰符作用示例代码
.listRowSeparator(_:)设置列表行分隔线样式(iOS 15+)。.listRowSeparator(.hidden)
.scrollIndicators(_:)控制滚动条显示(iOS 16.4+)。.scrollIndicators(.never)
.scrollTargetBehavior(_:)指定滚动行为(如分页滚动,iOS 17+)。.scrollTargetBehavior(.paging)

7. 导航与弹窗

修饰符作用示例代码
.navigationTitle(_:)设置导航栏标题。.navigationTitle("主页")
.toolbar(_:)自定义导航栏工具按钮。.toolbar { ToolbarItem(placement: .primaryAction) { Button("完成") {} } }
.alert(_:isPresented:actions:)显示弹窗。.alert("提示", isPresented: $showAlert) { Button("确定") {} }
.sheet(isPresented:content:)显示模态视图。.sheet(isPresented: $showSheet) { SettingsView() }

8. 高级功能

修饰符作用示例代码
.task(priority:_:)在视图出现时启动异步任务,视图消失时自动取消(iOS 15+)。.task { await loadData() }
.onChange(of:perform:)监听特定值的变化。.onChange(of: selectedTab) { print("Tab 切换为 (selectedTab)") }
.preference(key:value:)传递视图层级中的偏好值(如子视图尺寸)。.preference(key: SizePreference.self, value: geometry.size)

六、组合与最佳实践

1. 属性包装器组合示例

struct ContentView: View {
    @StateObject private var userModel = UserModel()
    @Environment(.colorScheme) var colorScheme
    @FocusState private var isInputFocused: Bool
    
    var body: some View {
        VStack {
            TextField("输入用户名", text: $userModel.name)
                .focused($isInputFocused)
                .padding()
                .background(colorScheme == .dark ? Color.gray : Color.white)
                .cornerRadius(8)
            
            Button("提交") {
                isInputFocused = false
            }
            .disabled(userModel.name.isEmpty)
        }
        .animation(.easeInOut, value: isInputFocused)
    }
}

2. 修饰符顺序的重要性

SwiftUI 的修饰符顺序会影响最终效果。例如:

Text("Hello")
    .padding()      // 先添加内边距
    .background(.red)  // 背景色会包含 padding 区域

七、总结

  • 属性包装器:用于数据流管理(@State@Binding)、状态共享(@EnvironmentObject)、UI 交互(@FocusState)。

  • 视图修饰符:调整布局(.frame.padding)、样式(.foregroundColor.font)、交互(.onTapGesture.disabled)。

  • 组合使用:通过合理组合修饰器和包装器,可以构建灵活、高效的 SwiftUI 界面。

  • 根据场景选择合适的工具:简单状态用 @State,跨视图共享用 @EnvironmentObject,持久化用 @AppStorage