原文地址:mecid.github.io/2019/06/12/…git
經過使用 @State 修飾器咱們能夠關聯出 View 的狀態. SwiftUI 將會把使用過 @State 修飾器的屬性存儲到一個特殊的內存區域,而且這個區域和 View struct 是隔離的. 當 @State 裝飾過的屬性發生了變化,SwiftUI 會根據新的屬性值從新建立視圖github
struct ProductsView: View {
let products: [Product]
@State private var showFavorited: Bool = false
var body: some View {
List {
Button(
action: { self.showFavorited.toggle() },
label: { Text("Change filter") }
)
ForEach(products) { product in
if !self.showFavorited || product.isFavorited {
Text(product.title)
}
}
}
}
}
複製代碼
這個例子裏咱們建立了一個列表,點擊按鈕 showFavorited 會發生值的取反操做,而後 SwiftUI 會經過最新的值更新值swift
譯者:這個 demo 在最新的 xcode 11 beta 6
中已經沒法運行起來了,由於 Button 組件的語法已經修改了xcode
有時候咱們會把一個視圖的屬性傳至子節點中,可是又不能直接的傳遞給子節點,由於在 Swift 中值的傳遞形式是值類型
傳遞方式,也就是傳遞給子節點的是一個拷貝過的值。可是經過 @Binding 修飾器修飾後,屬性變成了一個引用類型
,傳遞變成了引用傳遞,這樣父子視圖的狀態就能關聯起來了。session
struct FilterView: View {
@Binding var showFavorited: Bool
var body: some View {
Toggle(isOn: $showFavorited) {
Text("Change filter")
}
}
}
struct ProductsView: View {
let products: [Product]
@State private var showFavorited: Bool = false
var body: some View {
List {
FilterView(showFavorited: $showFavorited)
ForEach(products) { product in
if !self.showFavorited || product.isFavorited {
Text(product.title)
}
}
}
}
}
複製代碼
咱們在 FilterView 視圖裏用 @Binding 修飾 showFavorited 屬性, 在傳遞屬性是使用 $ 來傳遞 showFavorited 屬性的引用,這樣 FilterView 視圖就能讀寫父視圖 ProductsView 裏的狀態值了,而且值發生了修改 SwiftUI 會更新 ProductsView 和 FilterView 視圖app
譯者:在 FilterView 視圖裏,Toggle 組件的建立也使用 showFavorited, 直接 Text(showFavorited) 使用就行了ide
@ObservedObject 的用處和 @State 很是類似,從名字看來它是來修飾一個對象的,這個對象能夠給多個獨立的 View 使用。若是你用 @ObservedObject 來修飾一個對象,那麼那個對象必需要實現 ObservableObject 協議,而後用 @Published 修飾對象裏屬性,表示這個屬性是須要被 SwiftUI 監聽的ui
final class PodcastPlayer: ObservableObject {
@Published private(set) var isPlaying: Bool = false
func play() {
isPlaying = true
}
func pause() {
isPlaying = false
}
}
複製代碼
咱們定義了一個 PodcastPlayer 類,這個類能夠給不一樣的 View 使用,SwiftUI 會追蹤使用 View 裏通過 @ObservableObject 修飾過的對象裏進過 @Published 修飾的屬性變換,一旦發生了變換,SwiftUI 會更新相關聯的 UIspa
struct EpisodesView: View {
@ObservedObject var player: PodcastPlayer
let episodes: [Episode]
var body: some View {
List {
Button(
action: {
if self.player.isPlaying {
self.player.pause()
} else {
self.player.play()
}
}, label: {
Text(player.isPlaying ? "Pause": "Play")
}
)
ForEach(episodes) { episode in
Text(episode.title)
}
}
}
}
複製代碼
譯者:這個 demo 在最新的 xcode 11 beta 6
中已經沒法運行起來了,由於 Button 組件的語法已經修改了code
從名字上能夠看出,這個修飾器是針對全局環境的。經過它,咱們能夠避免在初始 View 時建立 ObservableObject, 而是從環境中獲取 ObservableObject
SceneDelegate.swift 文件
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let window = UIWindow(frame: UIScreen.main.bounds)
let episodes = [
Episode(id: 1, title: "First episode"),
Episode(id: 2, title: "Second episode")
]
let player = PodcastPlayer()
window.rootViewController = UIHostingController(
rootView: EpisodesView(episodes: episodes)
.environmentObject(player)
)
self.window = window
window.makeKeyAndVisible()
}
}
複製代碼
EpisodesView.swift 文件
struct EpisodesView: View {
@EnvironmentObject var player: PodcastPlayer
let episodes: [Episode]
var body: some View {
List {
Button(
action: {
if self.player.isPlaying {
self.player.pause()
} else {
self.player.play()
}
}, label: {
Text(player.isPlaying ? "Pause": "Play")
}
)
ForEach(episodes) { episode in
Text(episode.title)
}
}
}
}
複製代碼
能夠看出咱們獲取 PodcastPlayer 這個 ObservableObject 是經過 @EnvironmentObject 修飾器,可是在入口須要傳入 .environmentObject(player)
。@EnvironmentObject 的工做方式是在 Environment 查找 PodcastPlayer 實例。
繼續上面一段的說明,咱們的確開一個從 Environment 拿到用戶自定義的 object,可是 SwiftUI 自己就有不少系統級別的設定,咱們開一個經過 @Environment 來獲取到它們
struct CalendarView: View {
@Environment(\.calendar) var calendar: Calendar
@Environment(\.locale) var locale: Locale
@Environment(\.colorScheme) var colorScheme: ColorScheme
var body: some View {
return Text(locale.identifier)
}
}
複製代碼
經過 @Environment 修飾的屬性,咱們開一個監聽系統級別信息的變換,這個例子裏一旦 Calendar, Locale, ColorScheme 發生了變換,咱們定義的 CalendarView 就會刷新
謝謝