The original address: mecid. Making. IO / 2019/06/12 /…
@State
We can correlate the State of the View by using the @state decorator. SwiftUI will store attributes that use the @State modifier in a special memory area that is isolated from the View struct. When the @State decorated property changes, SwiftUI recreates the view based on the new property value
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)
}
}
}
}
}
Copy the code
In this example we create a list. Click showFavorited and the value is reversed. SwiftUI updates the value with the latest value
The demo doesn’t work in the latest Xcode 11 Beta 6 because the syntax of the Button component has been changed
@Binding
Sometimes we pass a view property to a child node, but we cannot pass it directly to the child node, because in Swift the value is passed as a value type, that is, a copied value is passed to the child node. But with the @Binding modifier, the property becomes a reference type and the pass becomes a reference pass, so that the state of the parent and child views can be correlated.
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)
}
}
}
}
}
Copy the code
In FilterView, we use @binding to modify the showFavorited property and $to pass a reference to the showFavorited property. SwiftUI will update the ProductsView and FilterView views
In the FilterView, the Toggle component is also createdShowFavorited, just Text
@ObservedObject
@observedobject is very similar to @State in that its name is used to decorate an object that can be used by multiple independent views. If you use @observedobject to modify an object, that object must implement the ObservableObject protocol, and then use @Published to modify the property in the object, indicating that the property needs to be listened on by SwiftUI
final class PodcastPlayer: ObservableObject {@Published private(set) var isPlaying: Bool = false
func play(a) {
isPlaying = true
}
func pause(a) {
isPlaying = false}}Copy the code
We define a PodcastPlayer class that can be used by different views. SwiftUI tracks @published property changes in the @ObservableObject modified object of the View. Once the changes are made, SwiftUI will update the associated UI
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)
}
}
}
}
Copy the code
The demo doesn’t work in the latest Xcode 11 Beta 6 because the syntax of the Button component has been changed
@EnvironmentObject
As the name suggests, this decorator is specific to the global environment. With this, we can avoid creating an ObservableObject in the initial View and instead get an ObservableObject from the environment
SceneDelegate. Swift file
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()
}
}
Copy the code
EpisodesView. Swift file
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)
}
}
}
}
Copy the code
You can see that we get the PodcastPlayer ObservableObject via the @environmentobject decorator, but in the entry we need to pass.environmentobject (Player). The way @environmentobject works is to find PodcastPlayer instances in Environment.
@Environment
SwiftUI does have a lot of system level Settings, so we need to get them via @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)
}
}
Copy the code
Using the @environment attribute, we need to listen for system-level changes. In this example, once the Calendar, Locale, and ColorScheme changes, our CalendarView will refresh
thank you