SwiftUI(iOS 14)新增属性包装器(Property Wrapper)

803 阅读2分钟

@StateObject

@StateObject 修饰的对象与 @ObservedObject 一样,都需要遵循 Observable 协议,功能也类似。区别在于,@StateObject 修饰的对象只会在所属的 View 中创建一次并在 View 的生命周期内存储相应的状态,而 @ObservedObject 修饰的对象会随着 View 的重绘生成新的对象,不会在 View 的生命周期内存储该对象的状态。

class Counter: ObservableObject {
  @Published var count: Int = 0
}


struct StateObjectView: View {
  
  @State private var buttonTitle = "Tap me"
  
  var body: some View {
    VStack {
      Group {
        Button(buttonTitle) {
          buttonTitle = buttonTitle == "Tap me" ? "Tapped" : "Tap me"
        }
        CounterView1()
        CounterView2()
      }
      .padding()
    }
  }
}


struct ItemList: View {
  @State private var items = ["hello", "world"]
  
  var body: some View {
    VStack {
      Button("Append item to list") {
        items.append("test")
      }
      
      List(items, id: \.self) { name in
        Text(name)
      }
      
      CounterView1()
      
      CounterView2()
    }
  }
}

struct CounterView1: View {
  @StateObject var counter1 = Counter()
  var body: some View {
    VStack {
      Text("StateObject count: \(counter1.count)")
      Button("IncrementStateObject") {
        counter1.count += 1
      }
    }
  }
}

struct CounterView2: View {
  @ObservedObject var counter2 = Counter()
  var body: some View {
    VStack {
      Text("ObservedObject count: \(counter2.count)")
      Button("IncrementStateObject") {
        counter2.count += 1
      }
    }
  }
}

如上代码所示,当我们点击 CounterView1 和 CounterView2 中的按钮时,会给相应的 Counter 实例的 count 属性加 1,一旦我们点击 "Tap me" 按钮,View 会执行重绘,这时 CounterView2 中的 count 会重置为 0,而 CounterView1 中使用 @StateObject 修饰的对象中的 count 仍然持有当前的数据状态。

那么我们应该在什么场景下分别使用这两个属性包装器呢?

在 View 的生命周期内,需要一直持有并存储对象的状态时,使用 @StateObject 修饰。基本上,绝大多数情况下的 viewModel 都会是这种情况。

只有在少数情况下,View 不需要一直持有该对象,该对象的状态会随着外界的条件改变而刷新自己时,我们才用到 @ObservedObject 修饰,比如上面的例子,如果我们要求点击 "Tap me" 按钮时,count 就重置,就需要用 @ObservedObject 修饰了。

@AppStorage

等同于 Userdefaults。

@SceneStorage

用于多窗口模式,相当于为每个场景持久化了一个场景值,每个场景只能读取该场景持久化的值。

当场景销毁时,该值也会销毁。建议存储轻量的、不敏感的数据。

@UIApplicationDelegateAdaptor

用来调用 AppDelegate 中的生命周期方法。

class AppDelegate: NSObject, UIApplicationDelegate {
	
}

@main
struct MyApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

其它参考

之前的文章SwiftUI 中常见的属性包装器(Property Wrapper)概览和今天这篇,只是列举了一些常用的,更多相关内容和使用方法可以参考下面两篇佳作:


Eul 是一款 SwiftUI 教程类 App(iOS、macOS),以文章(文字、图片、代码)配合真机示例(Xcode 12+、iOS 14+,macOS 11+)的形式呈现给读者。笔者意在尽可能使用简洁明了的语言阐述 SwiftUI 相关的知识,使读者能快速掌握并在 iOS 开发中实践。

下载链接:AppStore/Eul