我正在开发将滤镜应用于图像的应用程序。过滤器具有许多用户可以修改的参数。我创建了一个包含上述参数的ObservableObject。只要其中一个参数发生更改,即使视图显示与以前相同的值,视图也将进行可见更新。当我将参数建模为单个@State变量时,不会发生这种情况。
如果这是可以预期的(在所有观察到的对象做完之后,因此依赖于它的每个视图都会更新),那么ObservedObject是否是该工作的正确工具?另一方面,将参数建模为单独的@ 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())
}
}
}
答案 0 :(得分:1)
警告:此答案不理想。如果参数的属性将在另一个视图(例如,额外的选择器)中更新,则选择器视图将不更新。
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!")
}
}
答案 1 :(得分:0)
第二次尝试
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!")
}
}