我有以下模型对象,可用于为List
的每一行填充Toggle
,该行绑定到measurement.isSelected
final class Model: ObservableObject {
struct Measurement: Identifiable {
var id = UUID()
let name: String
var isSelected: Binding<Bool>
var selected: Bool = false
init(name: String) {
self.name = name
let selected = CurrentValueSubject<Bool, Never>(false)
self.isSelected = Binding<Bool>(get: { selected.value }, set: { selected.value = $0 })
}
}
@Published var measurements: [Measurement]
@Published var hasSelection: Bool = false // How to set this?
init(measurements: [Measurement]) {
self.measurements = measurements
}
}
只要任何hasSelection
为measurement.isSelected
,我都希望true
属性为true。我猜测Model
需要以某种方式观察measurements
中的更改,然后更新其hasSelection
属性…但是我不知道从哪里开始!
想法是将hasSelection
绑定到Button
以启用或禁用它。
Model
的用法如下……
struct MeasurementsView: View {
@ObservedObject var model: Model
var body: some View {
NavigationView {
List(model.measurements) { measurement in
MeasurementView(measurement: measurement)
}
.navigationBarTitle("Select Measurements")
.navigationBarItems(trailing: NavigationLink(destination: NextView(), isActive: $model.hasSelection, label: {
Text("Next")
}))
}
}
}
struct MeasurementView: View {
let measurement: Model.Measurement
var body: some View {
HStack {
Text(measurement.name)
.font(.subheadline)
Spacer()
Toggle(measurement.name, isOn: measurement.isSelected)
.labelsHidden()
}
}
}
有关信息,这是我要实现的屏幕截图。可选项目的列表,其中的导航链接在选择一个或多个项目时启用,在未选择任何项目时禁用。
答案 0 :(得分:1)
此刻,您的代码存在的问题是,即使您观察到measurements
的更改,由于选择将var isSelected: Binding<Bool>
声明为绑定对象,因此在选择内容更新时它们也不会得到更新。这意味着SwiftUI会将其存储在您的结构外部,并且结构本身不会更新(保持不变)。
您可以尝试在模型上声明@Published var selectedMeasurementId: UUID? = nil
,这样您的代码将如下所示:
import SwiftUI
import Combine
struct NextView: View {
var body: some View {
Text("Next View")
}
}
struct MeasurementsView: View {
@ObservedObject var model: Model
var body: some View {
let hasSelection = Binding<Bool> (
get: {
self.model.selectedMeasurementId != nil
},
set: { value in
self.model.selectedMeasurementId = nil
}
)
return NavigationView {
List(model.measurements) { measurement in
MeasurementView(measurement: measurement, selectedMeasurementId: self.$model.selectedMeasurementId)
}
.navigationBarTitle("Select Measurements")
.navigationBarItems(trailing: NavigationLink(destination: NextView(), isActive: hasSelection, label: {
Text("Next")
}))
}
}
}
struct MeasurementView: View {
let measurement: Model.Measurement
@Binding var selectedMeasurementId: UUID?
var body: some View {
let isSelected = Binding<Bool>(
get: {
self.selectedMeasurementId == self.measurement.id
},
set: { value in
if value {
self.selectedMeasurementId = self.measurement.id
} else {
self.selectedMeasurementId = nil
}
}
)
return HStack {
Text(measurement.name)
.font(.subheadline)
Spacer()
Toggle(measurement.name, isOn: isSelected)
.labelsHidden()
}
}
}
final class Model: ObservableObject {
@Published var selectedMeasurementId: UUID? = nil
struct Measurement: Identifiable {
var id = UUID()
let name: String
init(name: String) {
self.name = name
}
}
@Published var measurements: [Measurement]
init(measurements: [Measurement]) {
self.measurements = measurements
}
}
我不确定您希望导航栏中的导航按钮如何运行。现在,我只是在选择时将选择设置为nil。您可以根据需要进行修改。
如果要支持多选,则可以改用Set of selected ids。
此外,似乎iOS模拟器在导航方面存在一些问题,但我在物理设备上进行了测试,并且可以正常工作。
答案 1 :(得分:1)
@ user3441734 hasSelection在理想情况下应为get only属性,即 如果Measurement.isSelected中的任何一项为true,则为true
struct Data {
var bool: Bool
}
class Model: ObservableObject {
@Published var arr: [Data] = []
var anyTrue: Bool {
arr.map{$0.bool}.contains(true)
}
}
示例(如前)复制-粘贴-运行
import SwiftUI
struct Data: Identifiable {
let id = UUID()
var name: String
var on_off: Bool
}
class Model: ObservableObject {
@Published var data = [Data(name: "alfa", on_off: false), Data(name: "beta", on_off: false), Data(name: "gama", on_off: false)]
var bool: Bool {
data.map {$0.on_off} .contains(true)
}
}
struct ContentView: View {
@ObservedObject var model = Model()
var body: some View {
VStack {
List(0 ..< model.data.count) { idx in
HStack {
Text(verbatim: self.model.data[idx].name)
Toggle(isOn: self.$model.data[idx].on_off) {
EmptyView()
}
}
}
Text("\(model.bool.description)").font(.largeTitle).padding()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
更新model.data时
@Published var data ....
其发布者在objectWillChange
上致电ObservableObject
。
接下来,SwiftUI认识到ObservedObject
需要“更新”视图。视图将被重新创建,这将强制model.bool.description
具有新的价值。
最后更新
更改这部分代码
struct ContentView: View {
@ObservedObject var model = Model()
var body: some View {
NavigationView {
List(0 ..< model.data.count) { idx in
HStack {
Text(verbatim: self.model.data[idx].name)
Toggle(isOn: self.$model.data[idx].on_off) {
EmptyView()
}
}
}.navigationBarTitle("List")
.navigationBarItems(trailing:
NavigationLink(destination: Text("next"), label: {
Text("Next")
}).disabled(!model.bool)
)
}
}
}
在真实设备上尝试,否则NavigationLink只能使用一次(这是当前Xcode 11.3.1(11C504)中众所周知的模拟器错误)。