当 Publisher 参数没有要发出的任何新值时,为啥会调用 SwiftUI View 上的 onReceive 块?
Posted
技术标签:
【中文标题】当 Publisher 参数没有要发出的任何新值时,为啥会调用 SwiftUI View 上的 onReceive 块?【英文标题】:Why is onReceive block on SwiftUI View called when Publisher argument doesn't have any new values to emit?当 Publisher 参数没有要发出的任何新值时,为什么会调用 SwiftUI View 上的 onReceive 块? 【发布时间】:2020-05-15 02:12:13 【问题描述】:我已将 onReceive 修饰符附加到 SwiftUI 中的视图。订阅的目的是响应 @Published 属性 greeting
的更改,这是视图模型对象的一部分。
视图包含分段选取器。分段选取器使用视图模型中的另一个属性——index
。
莫名其妙地,当用户更改分段选择器选择时,即使发布者greeting
没有更改,也会调用 onReceive 块。
import SwiftUI
import Combine
class Model: ObservableObject
@Published var selectedIndex = 0
@Published var greeting = "initial"
struct ContentView: View
@ObservedObject var model: Model = Model()
let pickerTitles = ["One", "Two", "Three"]
var body: some View
VStack
return Picker("Options", selection: $model.selectedIndex)
ForEach(0 ..< pickerTitles.count) index in
Text(self.pickerTitles[index])
.pickerStyle(SegmentedPickerStyle())
.onReceive([model.greeting].publisher)str in
print(str)
发生了什么事?上面的代码是整个代码库。
回复:总结
我在 .onReceive 块中指定了一个发布者。那个指定的属性——greeting
——永远不会改变。当另一个属性——index
——被同一 ObservedObject 中的 Picker 更改时,.onReceive 块被神秘地调用。
【问题讨论】:
【参考方案1】:您在代码中使用Publishers.Sequence
,它将按顺序逐一发布元素。
使用您的代码,[model.greeting].publisher
会重新评估,并且每次评估 body
时都会重新创建发布者,这意味着发布的元素会重置为第一个。
通过下面的代码,你可以发现x
被打印出来,每次body
被评估:
struct ContentView: View
@ObservedObject var model: Model = Model()
let pickerTitles = ["One", "Two", "Three"]
var body: some View
VStack
return Picker("Options", selection: $model.selectedIndex)
ForEach(0 ..< pickerTitles.count) index in
Text(self.pickerTitles[index])
.pickerStyle(SegmentedPickerStyle())
.onReceive(["x"].publisher)str in
print(str)
如果您只想在greeting
上接收更改,您可以这样写:
struct ContentView: View
@ObservedObject var model: Model = Model()
let pickerTitles = ["One", "Two", "Three"]
var body: some View
VStack
return Picker("Options", selection: $model.selectedIndex)
ForEach(0 ..< pickerTitles.count) index in
Text(self.pickerTitles[index])
.pickerStyle(SegmentedPickerStyle())
.onReceive(model.$greeting)str in
print(str)
【讨论】:
我必须考虑这一点,但我想我理解你的观点,即每次用户在 Picker 中选择不同的段时,Combine 库都会观察视图模型的变化,然后核弹原始视图。重新初始化了一个新的视图结构。由于 .publisher 似乎默认为 CurrentSubject 发布者,因此在创建新 View 结构时会发出初始值(“初始值”)。谢谢你。我会到处玩。顺便说一句,转移到价值对象的世界是很棘手的。在 UIKit 中,几乎所有东西都是引用对象,并且不会突然“消失”。 @SmallTalk,我不确定你是否理解正确,但你的.publisher
放在一个数组上,所以它与CurrentValueSubject
无关。您的.publisher
调用publisher
property on Sequence
。您的代码 [model.greeting].publisher
会忽略模型的所有已发布或可观察的特征,并从 ["initial"]
创建一个 Publishers.Sequence
。
清除我有更多的理解差距。感谢您的评论。我现在要澄清的唯一一点是:如果 State 或 ObservedObject 中的 Published 属性发生变化,View 结构是否会被删除并从头开始重新初始化。似乎答案是肯定的。 (我知道 SwiftUI 会保留当前的 State 值,并且可能在丢弃 View 和使用保留值从头开始重新创建之间“重新连接”对 ObservedObject 的引用。这是 SwiftUI 中一个奇怪的新世界。
@SmallTalk,看来你说对了。 SwiftUI 在它认为需要的任何时候评估body
。返回的视图有点像蓝图,SwiftUI 将其与旧的body
进行比较,并更新已更改的实际可视组件。像我这样的老程序员可能需要大量的时间来适应它......
感谢您的帮助。我现在可以更好地理解了。以上是关于当 Publisher 参数没有要发出的任何新值时,为啥会调用 SwiftUI View 上的 onReceive 块?的主要内容,如果未能解决你的问题,请参考以下文章