从 SwiftUI 中的数组更新 List 中带有 Slider 值的文本 [英] Update text with Slider value in List from an array in SwiftUI
问题描述
我有一个滑块列表,但在更新显示滑块值的文本时遇到问题.
I have a list of sliders, but I have a problem updating the text that shows the slider value.
应用工作流程是这样的:
The app workflow is like this:
- 用户点按即可将新滑块添加到列表中.
- 创建一个定义滑块的对象并将其存储在一个数组中.
- 将数组作为属性 (Db) 的类是 ObservableObject,并为每个新项目触发 View 更新.
- 列表更新为新行.
到目前为止,一切都很好.每行都有一个滑块,其值存储在数组中对象的属性中.但是,值文本不会在滑块移动后立即更新,而是在添加新项目时更新.请看下面的 GIF:
So far, so good. Each row has a slider whose value is stored in a property in an object in an array. However, the value text doesn't update as soon as the slider is moved, but when a new item is added. Please see the GIF below:
如何将滑块移动绑定到文本值?我认为通过定义
How can I bind the slider movements to the text value? I thought that by defining
@ObservedObject var slider_value: SliderVal = SliderVal()
并将该变量绑定到滑块,该值将同时更新,但事实并非如此.非常感谢您的帮助.
and binding that variable to the slider, the value would be updated simultaneously but that is not the case. Thanks a lot for any help.
import SwiftUI
import Combine
struct ContentView: View {
@ObservedObject var db: Db
var body: some View {
NavigationView{
List(db.criteria_db){criteria in
VStack {
HStack{
Text(criteria.name).bold()
Spacer()
Text(String(criteria.slider_value.value)) //<-- Problem here
}
Slider(value: criteria.$slider_value.value, in:0...100, step: 1)
}
}
.navigationBarTitle("Criteria")
.navigationBarItems(trailing:
Button(action: {
Criteria.count += 1
db.criteria_db.append(Criteria(name: "Criteria(Criteria.count)"))
dump(db.criteria_db)
}, label: {
Text("Add Criteria")
})
)
}
.listStyle(InsetGroupedListStyle())
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(db: Db())
}
}
struct Criteria: Identifiable {
var id = UUID()
var name: String
@ObservedObject var slider_value: SliderVal = SliderVal()
static var count: Int = 0
init(name: String) {
self.name = name
}
}
class Db: ObservableObject {
@Published var criteria_db: [Criteria] = []
}
class SliderVal: ObservableObject {
@Published var value:Double = 50
}
推荐答案
@ObservableObject
不能在这样的 struct
中工作——它只在一个SwiftUI View
或 DynamicProperty
.对于您的用例,因为 class
是引用类型,所以 @Published
属性无法知道 SliderVal
已更改,因此所有者 View
永远不会更新.
The @ObservableObject
won't work within a struct
like that -- it's only useful inside a SwiftUI View
or a DynamicProperty
. With your use case, because the class
is a reference type, the @Published
property has no way of knowing that the SliderVal
was changed, so the owner View
never gets updated.
您可以通过将模型转换为 struct
来解决此问题:
You can fix this by turning your model into a struct
:
struct Criteria: Identifiable {
var id = UUID()
var name: String
var slider_value: SliderVal = SliderVal()
static var count: Int = 0
init(name: String) {
self.name = name
}
}
struct SliderVal {
var value:Double = 50
}
问题是,一旦您这样做了,您的 List
中就没有要使用的 Binding
.如果您有幸使用 SwiftUI 3.0(iOS 15 或 macOS 12),则可以在列表中使用 $criteria
来绑定到当前正在迭代的元素.
The problem, once you do this, is you don't have a Binding
to use in your List
. If you're lucky enough to be on SwiftUI 3.0 (iOS 15 or macOS 12), you can use $criteria
within your list to get a binding to the element being currently iterated over.
如果您使用的是早期版本,则需要使用索引来迭代项目,或者,我最喜欢的是,创建一个绑定到 id
的自定义绑定物品.它看起来像这样:
If you're on an earlier version, you'll need to either use indexes to iterate over the items, or, my favorite, create a custom binding that is tied to the id
of the item. It looks like this:
struct ContentView: View {
@ObservedObject var db: Db = Db()
private func bindingForId(id: UUID) -> Binding<Criteria> {
.init {
db.criteria_db.first { $0.id == id } ?? Criteria(name: "")
} set: { newValue in
db.criteria_db = db.criteria_db.map {
$0.id == id ? newValue : $0
}
}
}
var body: some View {
NavigationView{
List(db.criteria_db){criteria in
VStack {
HStack{
Text(criteria.name).bold()
Spacer()
Text(String(criteria.slider_value.value))
}
Slider(value: bindingForId(id: criteria.id).slider_value.value, in:0...100, step: 1)
}
}
.navigationBarTitle("Criteria")
.navigationBarItems(trailing:
Button(action: {
Criteria.count += 1
db.criteria_db.append(Criteria(name: "Criteria(Criteria.count)"))
dump(db.criteria_db)
}, label: {
Text("Add Criteria")
})
)
}
.listStyle(InsetGroupedListStyle())
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(db: Db())
}
}
class Db: ObservableObject {
@Published var criteria_db: [Criteria] = []
}
现在,因为模型都是值类型(struct
s),View
和 @Published
知道何时更新和您的滑块按预期工作.
Now, because the models are all value types (struct
s), the View
and @Published
know when to update and your sliders work as expected.
这篇关于从 SwiftUI 中的数组更新 List 中带有 Slider 值的文本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!