[英]SwiftUI ObservedObject causes undesirable visible view updates
我正在開發一個將過濾器應用於圖像的應用程序。 過濾器有許多用戶可以修改的參數。 我創建了一個包含所述參數的 ObservableObject。 每當其中一個參數發生變化時,視圖都會有可見的更新,即使視圖顯示的值與以前相同。 當我將參數作為單獨的@State 變量時,這不會發生。
如果這是意料之中的(畢竟觀察到的 object確實發生了變化,所以依賴於它的每個視圖都會更新),ObservedObject 是適合這項工作的工具嗎? 另一方面,model 將參數作為單獨的@State/@Binding 變量似乎非常不方便,特別是如果需要將大量參數(例如10+)傳遞給多個子視圖!
因此我的問題是:
我在這里正確使用 ObservedObject 嗎? 可見更新是無意的,但可以接受,還是在 swiftUI 中有更好的解決方案來處理這個問題?
使用@ObservedObject 的示例:
import SwiftUI
class Parameters: ObservableObject {
@Published var pill: String = "red"
@Published var hand: String = "left"
}
struct ContentView: View {
@ObservedObject var parameters = Parameters()
var body: some View {
VStack {
// Using the other Picker causes a visual effect here...
Picker(selection: self.$parameters.pill, label: Text("Which pill?")) {
Text("red").tag("red")
Text("blue").tag("blue")
}.pickerStyle(SegmentedPickerStyle())
// Using the other Picker causes a visual effect here...
Picker(selection: self.$parameters.hand, label: Text("Which hand?")) {
Text("left").tag("left")
Text("right").tag("right")
}.pickerStyle(SegmentedPickerStyle())
}
}
}
使用@State 變量的示例:
import SwiftUI
struct ContentView: View {
@State var pill: String = "red"
@State var hand: String = "left"
var body: some View {
VStack {
Picker(selection: self.$pill, label: Text("Which pill?")) {
Text("red").tag("red")
Text("blue").tag("blue")
}.pickerStyle(SegmentedPickerStyle())
Picker(selection: self.$hand, label: Text("Which hand?")) {
Text("left").tag("left")
Text("right").tag("right")
}.pickerStyle(SegmentedPickerStyle())
}
}
}
警告:這個答案不太理想。 如果參數的屬性將在另一個視圖中更新(例如,額外的選擇器),則不會更新選擇器視圖。
ContentView 不應“觀察”參數; 參數的更改將導致它更新其內容(在 Pickers 的情況下可見)。 為了避免需要觀察到的屬性包裝器,我們可以為參數的屬性提供顯式綁定。 ContentView 的子視圖可以在參數上使用@Observed。
import SwiftUI
class Parameters: ObservableObject {
@Published var pill: String = "red"
@Published var hand: String = "left"
}
struct ContentView: View {
var parameters = Parameters()
var handBinding: Binding<String> {
Binding<String>(
get: { self.parameters.hand },
set: { self.parameters.hand = $0 }
)
}
var pillBinding: Binding<String> {
Binding<String>(
get: { self.parameters.pill },
set: { self.parameters.pill = $0 }
)
}
var body: some View {
VStack {
InfoDisplay(parameters: parameters)
Picker(selection: self.pillBinding, label: Text("Which pill?")) {
Text("red").tag("red")
Text("blue").tag("blue")
}.pickerStyle(SegmentedPickerStyle())
Picker(selection: self.handBinding, label: Text("Which hand?")) {
Text("left" ).tag("left")
Text("right").tag("right")
}.pickerStyle(SegmentedPickerStyle())
}
}
}
struct InfoDisplay: View {
@ObservedObject var parameters: Parameters
var body: some View {
Text("I took the \(parameters.pill) pill from your \(parameters.hand) hand!")
}
}
第二次嘗試
ContentView 不應觀察參數(這會導致不希望的可見更新)。 參數的屬性也應該是 ObservableObjects 以確保視圖可以在特定屬性更改時更新。
由於字符串是結構,它們不能符合 ObservableObject; 需要一個小的包裝器“ObservableValue”。
MyPicker 是 Picker 的一個小型包裝器,用於在更改時更新視圖。 默認 Picker 接受綁定,因此依賴於層次結構的視圖來執行更新。
這種方法感覺可擴展:
缺點:
import SwiftUI
import Combine
class ObservableValue<Value: Hashable>: ObservableObject {
@Published var value: Value
init(initialValue: Value) {
value = initialValue
}
}
struct MyPicker<Value: Hashable, Label: View, Content : View>: View {
@ObservedObject var object: ObservableValue<Value>
let content: () -> Content
let label: Label
init(object: ObservableValue<Value>,
label: Label,
@ViewBuilder _ content: @escaping () -> Content) {
self.object = object
self.label = label
self.content = content
}
var body: some View {
Picker(selection: $object.value, label: label, content: content)
.pickerStyle(SegmentedPickerStyle())
}
}
class Parameters: ObservableObject {
var pill = ObservableValue(initialValue: "red" )
var hand = ObservableValue(initialValue: "left")
private var subscriber: Any?
init() {
subscriber = pill.$value
.combineLatest(hand.$value)
.sink { _ in
self.objectWillChange.send()
}
}
}
struct ContentView: View {
var parameters = Parameters()
var body: some View {
VStack {
InfoDisplay(parameters: parameters)
MyPicker(object: parameters.pill, label: Text("Which pill?")) {
Text("red").tag("red")
Text("blue").tag("blue")
}
MyPicker(object: parameters.hand, label: Text("Which hand?")) {
Text("left").tag("left")
Text("right").tag("right")
}
}
}
}
struct InfoDisplay: View {
@ObservedObject var parameters: Parameters
var body: some View {
Text("I took the \(parameters.pill.value) pill from your \(parameters.hand.value) hand!")
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.