首页 > 解决方案 > 当 Publisher 参数没有要发出的任何新值时,为什么会调用 SwiftUI View 上的 onReceive 块?

问题描述

我已将 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)
        }

    }
}

这是怎么回事?上面的代码是整个代码库。

RE:摘要我在 .onReceive 块中指定了一个发布者。那个指定的属性greeting——永远不会改变。当同一个 ObservedObject 中的 Picker 更改另一个属性 -- index-- 时,.onReceive 块被神秘地调用。

在此处输入图像描述

标签: swiftswiftui

解决方案


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)
        }
    }
}

推荐阅读