jto*_*uzy 1 swift reactive swiftui combine
我想显示多个文本字段,代表比赛每个部分的分数。
示例:对于排球比赛,我们有 25/20、25/22、25/23。总体得分为 3/0。
全局组件架构:
>> ParentComponent
>> MainComponent
>> X TextFieldsComponent (2 text fields, home/visitor score)
Run Code Online (Sandbox Code Playgroud)
最底层的组件 TextFieldsComponent 包含基本绑定:
struct TextFieldsComponent: View {
@ObservedObject var model: Model
class Model: ObservableObject, Identifiable, CustomStringConvertible {
let id: String
@Published var firstScore: String
@Published var secondScore: String
var description: String {
"\(firstScore) \(secondScore)"
}
init(id: String, firstScore: String = .empty, secondScore: String = .empty) {
self.id = id
self.firstScore = firstScore
self.secondScore = secondScore
}
}
var body: some View {
HStack {
TextField("Dom.", text: $model.firstScore)
.keyboardType(.numberPad)
TextField("Ext.", text: $model.secondScore)
.keyboardType(.numberPad)
}
}
}
Run Code Online (Sandbox Code Playgroud)
父组件需要显示比赛所有部分的总得分。我想尝试组合绑定/流来获得总分。
我尝试了多种解决方案,最终得到了这个不起作用的代码(reduce似乎没有获取数组的所有元素,而是在内部存储了以前的结果):
struct MainComponent: View {
@ObservedObject var model: Model
@ObservedObject private var totalScoreModel: TotalScoreModel
class Model: ObservableObject {
@Published var scores: [TextFieldsComponent.Model]
init(scores: [TextFieldsComponent.Model] = [TextFieldsComponent.Model(id: "main")]) {
self.scores = scores
}
}
private final class TotalScoreModel: ObservableObject {
@Published var totalScore: String = ""
private var cancellable: AnyCancellable?
init(publisher: AnyPublisher<String, Never>) {
cancellable = publisher.print().sink {
self.totalScore = $0
}
}
}
init(model: Model) {
self.model = model
totalScoreModel = TotalScoreModel(
publisher: Publishers.MergeMany(
model.scores.map {
Publishers.CombineLatest($0.$firstScore, $0.$secondScore)
.map { ($0.0, $0.1) }
.eraseToAnyPublisher()
}
)
.reduce((0, 0), { previous, next in
guard let first = Int(next.0), let second = Int(next.1) else { return previous }
return (
previous.0 + (first == second ? 0 : (first > second ? 1 : 0)),
previous.1 + (first == second ? 0 : (first > second ? 0 : 1))
)
})
.map { "[\($0.0)] - [\($0.1)]" }
.eraseToAnyPublisher()
)
}
var body: some View {
VStack {
Text(totalScoreModel.totalScore)
ForEach(model.scores) { score in
TextFieldsComponent(model: score)
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
我正在寻找一种解决方案来获取每个绑定更改的事件,并将其合并到单个流中,以将其显示在 MainComponent 中。
注意:TextFieldsComponent 也需要可以独立使用。
MergeMany这是正确的方法,正如您自己开始的那样,尽管我认为您使事情变得过于复杂。
如果您想在视图中显示总分(假设总分由Model而非拥有TotalScoreModel,这是有道理的,因为它拥有基础分数),那么您需要表明此模型将在以下情况发生变化:任何基础分数都会改变。
然后,您可以将总分作为计算属性提供,SwiftUI 将在重新创建视图时读取更新后的值。
class Model: ObservableObject {
@Published var scores: [TextFieldsComponent.Model]
var totalScore: (Int, Int) {
scores.map { ($0.firstScore, $0.secondScore) }
.reduce((0,0)) { $1.0 > $1.1 ? ( $0.0 + 1, $0.1 ) : ($0.0, $0.1 + 1) }
}
private var cancellables = Set<AnyCancellable>()
init(scores: [TextFieldsComponent.Model] = [.init(id: "main")]) {
self.scores = scores
// get the ObservableObjectPublisher publishers
let observables = scores.map { $0.objectWillChange }
// notify that this object will change when any of the scores change
Publishers.MergeMany(observables)
.sink(receiveValue: self.objectWillChange.send)
.store(in: &cancellables)
}
}
Run Code Online (Sandbox Code Playgroud)
然后,在视图中,您可以照常使用Model.totalScore:
@ObservedObject var model: Model
var body: some View {
Text(model.totalScore)
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3150 次 |
| 最近记录: |