Property Wrappers – tems

  • @State特点包装器
  • @StateObject特点包装器
  • @Published特点包装器
  • @ObservedObject特点包装器
  • @EnvironmentObject特点包装器
  • @Environment特点包装器
  • @Binding特点包装器

概述

文章首要同享SwiftUI Modifier的学习进程,将运用事例的方式进行说明。内容浅显易懂,Property Wrappers items介绍详细特点包装器,倾向理论,能够移步Github下载code -> github事例链接

1、@State特点包装器

SwiftUI运用@State特点包装器能够修正结构体中的值,在结构体中,由于结构体是值类型,默许状况下是不允许的。

运用@State润饰特点时,实际上将其存储在结构体中移除,并放入由SwiftUI管理的同享缓存中。这代表SwiftUI能够依据需求毁掉并从头创立结构体(这在程序运行中可能会产生许多次!),而不会丢掉它正在存储的状况。

struct FFPropertyWrapperState: View {
    //由于@State的特性是存储在SwiftUI的同享缓存中,Apple建议运用@State润饰的变量最好符号为private
    @State private var prompt = "meta BBLv"
    //当然,这不是有必要选项,你也能够不运用private界说
    @State var hobby = "KEEP LOVING, KEEP LIVING"
    //当运用@State润饰引证类型的时分,数据产生更改时,不会收到告知,对于不契合ObservableObject协议的类有用。
    var body: some View {
        Text("Hello, World!")
    }
}

2、@StateObject特点包装器

SwiftUI的@StateObject特点包装器旨在添补状况管理中的一个非特定的空白:当你需求在视图内创立一个引证类型并保证它在该视图与其他与之同享的视图中保持active状况时。

//创立一个User类,并遵守obserVableObject
class StateObjectUser: ObservableObject {
    var username = "meta BBLv"
}
struct FFPropertyWrapperStateObject: View {
    //假如想在视图中运用它,能够在SwiftUI外部创立并注入,或许在SwiftUI中创立并运用@StateObject
    @StateObject var user = StateObjectUser()
    var body: some View {
        Text("Username: \(user.username)")
        //这将保证user实例视图更新时不会被毁掉
    }
}

以前,能够运用@ObservedObject来获得相同的结果,但这是不安全的操作。在极小的概率下@ObservedObject可能会意外开释它存储的目标,由于它并没有被规划成目标的最终数据源。说人话便是它不持有数据,不对数据担任。而@StateObject则不会出现此问题,所以引证类型要运用@StateObject润饰。

重要提示:在创立视图时担任对目标的创立,运用@StateObject创立的目标应该被创立一次,然后其他同享的视图运用@ObservedObject来同享目标。

3、@Published特点包装器

@Published是SwiftUI最重要的特点包装器之一,由于能够创立可调查目标,主动在产生更改时进行告知。SwiftUI会主动监听这些更改,并从头调用依赖于数据的视图的body特点,本质上会刷新视图。

在实际操作中,这意味着每当带有@Published符号特点的目标产生更改时,所有运用该目标的视图都会从头加载来push这些更改。

3.1、默许创立特点

Bag契合ObservableObject协议,那么SwiftUI的特点能够调查它的更改。但是,由于他的特点items并没有运用@Published符号,所以永远不会发送更改告知,这个时分,你能够自由的想数组增加数据,但不会主动更新视图。

//创立一个可调查目标
class Bag: ObservableObject {
    var items = [String]()
}

3.2、运用@Published符号特点

//假如希望它能够承受告知,只需求增加@Published就好了
class BagPublished: ObservableObject {
    @Published var items = [String]()
}

这种状况下,不需求执行任何其他操作,@Published特点包装器实际上会为items增加一个willSet特点调查器,以便任何更改都会主动发送给调查者。

@Published是一种选择性的办法,需求依据需求列出哪些特点更改应该发送告知,然后运用@Published润饰,默许状况下,未运用@Published创立的变量在变化时是不会引起视图的从头加载的。比如在存储缓存、内部运用特点,这些就值的更改就没必要更新视图。

4、@ObservedObject特点包装器

SwiftUI供给了@ObservedObject特点包装器,以便视图能够关在外部目标的状况,并在重要的更改产生时得到告知。它在行为上相似@StateObject,但不能用于创立目标,只能运用@ObservedObject润饰已在其他当地创立的目标,以便利同享数据。假如意外运用了@ObservedObject创立了目标,那么SwiftUI可能会意外毁掉目标造成crash。

class Order: ObservableObject {
    @Published var items = [String]()
}
struct FFPropertyWrapperObservedObject: View {
    @ObservedObject var order: Order
    var body: some View {
        Text("Hello, World!")
    }
}

Order类运用@Published润饰了items,因而当items产生更改时,他将主动发送告知,而FFPropertyWrapperObservedObject运用@ObservedObject来监听这些告知,假如没有@ObservedObject,更改告知依然会发送,但是会被当时视图疏忽。

虽然看起来很简单,但值得深入了解一些详细细节:

  1. 首要,运用@ObservedObject符号的任何类型都有必要契合ObservableObject协议,这意味着它有必要是一个类而不是结构体,这是规定,SwiftUI要起此处有必要运用class

  2. 其次,被调查目标专门规划用于在外部调查你的视图的目标,这意味着它能够在多个视图之间同享。@ObservedObject特点包装器将主动保证特点收到密切监督,以便重要的更改出现时从头加载运用它的任何视图。这还代表了数据有必要在其他当地被创立,是被传入你的视图的。

  3. 第三,不是被调查的目标中所有的特点都会导致视图刷新。由你的需求决议了哪些特点更改会发送更改告知,要么运用@Published,要么自界说告知,都能够实现。契合ObservableObject协议的类型会得到一个默许的objectWillChange的发布者,能够依据需求进行自界说告知。

5、@EnvironmentObject特点包装器

SwiftUI的@EnvironmentObject特点包装器能够创立依赖于同享数据的视图,通常跨足整个SwiftUI运用程序。例如,假如创立一个将在运用程序的许多部分之间同享的用户目标,那么运用@EnvironmentObject

//它契合ObserveableObject协议,这代表了能够运用@Observedobject或@EnvironmentObject来润饰目标。
class EnvironmentOrder: ObservableObject {
    @Published var items = [String]()
}
struct FFPropertyWrapperEnvironmentObject: View {
    //经过@EnvironmentObject来润饰目标,这是一个同享数据,也便是本身并不持有数据。
    @EnvironmentObject var order: Order
    var body: some View {
        Text("Hello, World!")
    }
}

order特点并没有经过@EnvironmentObject初始化,并给出默许值。在当时这个视图中,该值由SwiftUI环境供给,而不是由此视图显现创立。@EnvironmentObject和@ObservedObject有许多相似之处:

  • 两者都有必要引证遵守OvservableObject协议的class
  • 两者能够夸多个视图同享数据
  • 在运用@Published特点包装器润饰的特点产生更改时,都会更新任何引证此数据的视图。
  • 运用@EnvironmentObject的目标将由外部实体供给,而不是由当时视图创立。

实际运用场景:将视图A的数据传递到视图E,中心间隔了视图(B、C和D)假如运用@ObservedObject需求链式传递,不管中心视图(B、C和D)是否需求数据,都也被动接纳, 传递链条:A->B->C->D->E

当运用@EnvironmentObject时,视图A创立一个目标并将其放入环境中,然后,内部的任何视图都能够在需求时随时访问该环境目标,只需求对环境请求即可,不需求显现传递,传递链条:A(创立并放入Environment)-> E (直接在Environment中读取)

反常点:当显现运用@EnvironmentObject的视图时,SwiftUI将立即在环境中搜索正确类型的目标。假如找不到(比如忘掉放入到Environment中),那么运用程序将立即crash。也就代表着,当你运用@EnvironmentObject时,你实际上是在告知SwiftUI,我现已将目标存在环境中了,相似运用隐式解包。

6、@Environment特点包装器

SwiftUI供给了@Environment和@EnvironmentObject两个特点包装器,但他们在细节上有一些不同:而@Environmentobject能够将值注入到环境中,@Environment特别用于与SwiftUI自己预界说的key一同运用。

struct FFPropertyWrapperEnvironment: View {
    //例如,@Environment很合适读取CoreData托管目标的context、设备是否处于深色形式或淡色形式、烘托视图时运用的大些类别等固定特点,这些特点来自系统
    @Environment(\.horizontalSizeClass) var horizontalSizeClass
    @Environment(\.managedObjectContext) var managedObjectContext
    //@Environment作用是在环境中读取目标
    @EnvironmentObject var order: EnvironmentOrder
    //这个差异看起来很小,但由于@EnvironmentObject的实现方式,它非常重要。当我们说order的类型是EnvironmentOrder时,SwiftUI将查找其环境以找到该类型的目标并将其附加到order特点上。但是,运用@Environment时,同样的行为不太可能产生,由于许多东西可能同享相同的数据类型。
    @Environment(\.accessibilityReduceMotion) var reduceMotion
    @Environment(\.accessibilityReduceTransparency) var reduceTransparency
    @Environment(\.accessibilityEnabled) var accessibilityEnabled
    //这三个环境key都回来一个bool值,所以假如不明确指定是那个Key,就无法正确读取他们。
    var body: some View {
        Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
    }
}

6.1、@Environment

例如,@Environment很合适读取CoreData托管目标的context、设备是否处于深色形式或淡色形式、烘托视图时运用的大写类别等固定特点,这些特点来自系统。@Environment作用是在环境中读取目标

struct FFPropertyWrapperEnvironment: View {
    @Environment(\.horizontalSizeClass) var horizontalSizeClass
    @Environment(\.managedObjectContext) var managedObjectContext
}

但是,运用@Environment时,同引证类型有很大差异,由于许多东西可能同享相同的数据类型。这三个环境key都回来一个bool值,所以假如不明确指定是那个Key,就无法正确读取他们。

struct FFPropertyWrapperEnvironment: View {
    @Environment(\.accessibilityReduceMotion) var reduceMotion
    @Environment(\.accessibilityReduceTransparency) var reduceTransparency
    @Environment(\.accessibilityEnabled) var accessibilityEnabled
}

6.2、@EnvironmentObject

@EnvironmentObject就不需求制定详细的Key了,相关到运用类型的目标即可。这个差异看起来很小,但由于@EnvironmentObject的实现方式,它非常重要。当order的类型是EnvironmentOrder时,SwiftUI将查找其环境以找到该类型的目标并将其附加到order特点上。

struct FFPropertyWrapperEnvironment: View {
    @EnvironmentObject var order: EnvironmentOrder
}

7、@Binding特点包装器

@Binding声明一个值来自其他当地,而且应该在这两个当地同享运用。这与@ObservedObject和@EnvironmentObject不同,后者都是规划用于在多个视图之间同享引证类型。

//创立AddView视图
struct AddView: View {
    @Binding var isPresented: Bool
    var body: some View {
        Button("封闭") {
            isPresented = false
        }
    }
}
//创立了FFPropertyWrapperBinding视图,其间再创立一个包括@State的特点,用于存储一个子视图是否正在显现。
struct FFPropertyWrapperBinding: View {
    @State private var showingAddUser = false
    var body: some View {
        VStack {
            //代码区域
            Button("翻开用户中心") {
                showingAddUser = true
            }
        }
        .sheet(isPresented: $showingAddUser) {
            //显现增加用户视图
            AddView(isPresented: $showingAddUser)
            //整个操作能够理解为FFPropertyWrapperBinding和AddView同享同一个Bool值,
            //当他在一个当地产生变化时,也会在另外一个当地产生改变。
        }
    }
}

运用showingAddUser作为sheet的isPresented参数,假如bool值更改为true时,将显现present的视图。而且被presnt的视图能够自己封闭,就需求将showingAddUser传递到present的视图。

期待的状况是,present出来的用户视图能够将showingAddUser设置为false,那么FFPropertyWrapperBinding视图就会躲藏它,以达到封闭当时present视图的目的。那么运用@Binding就非常完美,由于它能够在增加用户视图中创立一个特点,表示“这个值来自其他当地,而且在我们之间同享状况。”

调试结果

我并没有为AddView增加dismiss相关函数,完全是靠showingAddUser的bool值来控制的dismiss

SwiftUI基础篇Property Wrappers - Items(上)