Understanding Property Wrappers
Published by @SoNiceInfo at 6/24/2020
Property Wrappers monitor the state of your variables in SwiftUI.
Property Wrappers allow you to share a variable between multiple Views and redraw the View when the variable is updated.
The SwiftUI comes with the following types of Property Wrappers
Property Wrappers | Role | Uses(example) |
---|---|---|
@State | Monitoring the state of a View and the value of a variable. | Monitoring Sheet (modal) status. |
@Binding | Referencing and modifying another View's @State variable. | Passing variables to a Sheet (modal). |
@ObservedObject | Monitoring for structure/class changes. | Handling classes with multiple properties, such as user information. |
@Environment | Obtaining predefined environment information about the View. | Dealing with the size of the screen. |
@EnvironmentObject | Monitoring for struct/class changes. | Handling a class with multiple properties. |
@State
@State
can hold a single value.@State
should be used only within a single view.
In the example, it's used to hold the sheet (modal) transition state in the View.
I'm adding $ (the binding of isPresented) to pass.
//
// ContentView.swift
//
import SwiftUI
struct ContentView: View {
@State private var isPresented: Bool = false
@State private var catName: String = "Tora"
var body: some View {
VStack {
Text(catName)
Button("Show Result") {
self.isPresented.toggle()
}
}
.sheet(isPresented: $isPresented) {
VStack {
TextField("My cat's name is...", text: self.$catName)
.multilineTextAlignment(.center)
Button("Close Result") {
self.isPresented.toggle()
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
@Binding
You can use @Binding
to inherit another View's @State
variable.
If you change a variable that you have made @Binding
, other Views will be notified of the change.
//
// ContentView.swift
//
import SwiftUI
struct SheetView: View {
@Binding var isPresented: Bool
@Binding var catName: String
var body: some View {
VStack {
TextField("My cat's name is...", text: $catName)
.multilineTextAlignment(.center)
Button("Close Result") {
self.isPresented.toggle()
}
}
}
}
struct ContentView: View {
@State private var isPresented: Bool = false
@State private var catName: String = "Tora"
var body: some View {
VStack {
Text(catName)
Button("Show Result") {
self.isPresented.toggle()
}
}
.sheet(isPresented: $isPresented) {
SheetView(isPresented: self.$isPresented, catName: self.$catName)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
@ObservedObject
@ObservedObject
is useful for monitoring the value of a structure or class.
Multiple instances can be created with ObservableObject
protocol-compliant classes.
With @Published
variables, it's changes are notified to the multiple views.
//
// ContentView.swift
//
import SwiftUI
class CatModel: ObservableObject {
@Published var name : String
@Published var age : String
init (name: String, age: String) {
self.name = name
self.age = age
}
}
struct ResultView: View {
@Binding var isPresented: Bool
@ObservedObject var cat: CatModel
var body: some View {
VStack {
Text("My cat is: \(cat.name), \(cat.age)")
Button("Close Result") {
self.isPresented.toggle()
}
}
}
}
struct ContentView: View {
@State private var isPresented: Bool = false
@ObservedObject var cat = CatModel(name: "Mike", age: "3")
var body: some View {
VStack {
HStack {
Text("Name: ")
TextField("Input name", text: $cat.name)
}
HStack {
Text("Age: ")
TextField("Input age", text: $cat.age)
}
Button("Show Result") {
self.isPresented.toggle()
}
}
.sheet(isPresented: $isPresented) {
ResultView(isPresented: self.$isPresented, cat: self.cat)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
@Environment
@Environment
can retrieve environment information such as font, dark mode and light mode.
Please see what you can retrive here EnvironmentValues - SwiftUI | Apple Developer Documentation.
//
// ContentView.swift
//
import SwiftUI
struct ContentView: View {
@Environment(\.font) var font
@Environment(\.colorScheme) var colorScheme
var body: some View {
VStack {
Text("Text")
.font(font)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
HStack {
ContentView()
.environment(\.font, .body)
ContentView()
.environment(\.font, .largeTitle)
}
}
}
@EnvironmentObject
@EnvironmentObject
can hold multiple values in it.@EnvironmentObject
is available if you want to use it on all views.
While @ObservedObject
can create multiple instances, @EnvironmentObject
can create a single instance.
iOS 13 Support
//
// SceneDelegate.swift
//
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Create the SwiftUI view that provides the window contents.
// 大事!!
let contentView = ContentView().environmentObject(CatModel())
...
}
}
iOS 14 Support
import SwiftUI
@main
struct iOS14App: App {
var body: some Scene {
WindowGroup {
ContentView().environmentObject(CatModel())
}
}
}
Common
//
// ContentView.swift
//
import SwiftUI
class CatModel: ObservableObject {
@Published var name = ""
@Published var age = ""
}
struct ContentView: View {
@State var isPresented: Bool = false
@EnvironmentObject var cat : CatModel
var body: some View {
VStack {
HStack {
Text("Name: ")
TextField("Input name", text: $cat.name)
}
HStack {
Text("Age: ")
TextField("Input age", text: $cat.age)
}
Button("Show Result") {
self.isPresented.toggle()
}
}
.sheet(isPresented: $isPresented) {
ResultView(isPresented: self.$isPresented)
.environmentObject(self.cat)
}
}
}
struct ResultView: View {
@Binding var isPresented: Bool
@EnvironmentObject var cat : CatModel
var body: some View {
VStack {
Text("My cat is: \(cat.name), \(cat.age)")
Button("Close Result") {
self.isPresented.toggle()
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}