SwiftUI:ViewModifier不会监听onReceive事件

6

我有一个自定义的ViewModifier,它仅仅是在原来的内容上添加了一个onReceive修饰符,但onReceive却没有被触发。这里有一段示例代码可以复制、粘贴并在XCode中运行:

import SwiftUI
import Combine

class MyViewModel: ObservableObject {
    @Published var myProperty: Bool = false
}
struct ContentView: View {
    @ObservedObject var viewModel: MyViewModel

    var body: some View {
        Text("Hello, World!")
        .modifier(MyOnReceive(viewModel: viewModel))
            .onTapGesture {
                self.viewModel.myProperty = true
        }
    }
}

struct MyOnReceive: ViewModifier {
    @ObservedObject var viewModel: MyViewModel

    func body(content: Content) -> some View {
        content
            .onReceive(viewModel.$myProperty) { theValue in
                print("The Value is \(theValue)") // <--- this is not executed
        }
    }
}

SwiftUI 是否被设计成不允许在 ViewModifier 中执行 onReceive,还是这是一个 bug?在我的实际项目中有一个视图,里面放了一些业务逻辑在 onReceive 中导致视图变得越来越大,因此我需要通过将它与 onReceive 分离来清理视图。


在SwiftUI中应用视图模型模式是非常糟糕的做法。 - malhal
1
@malhal SwiftUI 自然地与这种模式配合工作。 - JAHelia
1
抱歉,这不是正确的模式。希望我在这里回答你的问题能让你重新回到 SwiftUI 的正确轨道上:https://dev59.com/B73pa4cB1Zd3GeqPYCLc#70012628 - malhal
@malhal,感谢您在那个答案中的澄清。 - JAHelia
有趣的模式。我偶然发现了这个。我使用类似的模式,其中一个ViewModifier需要一种“VieModel”(您可以将其命名为“Store”,或者AppState或任何您想要的名称),或者说它是接收外部事件的某个actor(不是用户意图!),根据其状态和事件执行一些逻辑,然后发布一个状态,该状态确定是否会呈现几个表格中的一个以及如果有,使用哪个内容。 - CouchDeveloper
2个回答

20

好的,这对我来说可行:

func body(content: Content) -> some View {
    content
        .onAppear()   // <--- this makes it work
        .onReceive(viewModel.$myProperty) { theValue in
            print("-----> The Value is \(theValue)") // <--- this will be executed
    }
}

它确实能够工作,但我不明白为什么,我认为这被视为一种解决方法。 - JAHelia
我是通过试错发现这个的。我也不理解,而且我不知道这是一个已知的解决方法。 - workingdog support Ukraine
如果可以的话,请给这个问题点赞,以便在将来能够引起一些关注。 - JAHelia
4
这个.onAppear()会在视图出现后延迟发布者事件。 - Fero

1

ObservableObject@Published是Combine框架的一部分,如果您没有使用Combine,则不应该使用类来处理视图数据。相反,您应该按照设计使用SwiftUI,并避免使用重型对象,而是将数据放入高效的View数据结构中或按以下方式创建自定义结构:

import SwiftUI

struct MyConfig {
    var myProperty: Bool = false

    mutating func myMethod() {
         myProperty = !myProperty
    }
}
struct ContentView: View {
    @State var config = MyConfig()

    var body: some View {
        Text("Hello, World!")
        .onTapGesture {
            config.myMethod()
        }
    }
}

尝试使用onChange而不是onchange

https://developer.apple.com/documentation/swiftui/scrollview/onchange(of:perform:)

.onChange(of: viewModel.myProperty) { newValue in
        print("newValue \(newValue)")
    }

但请不要在SwiftUI中使用视图模型对象模式,尝试强制自己使用值类型,例如结构体来处理所有数据,因为SwiftUI是为此而设计的。属性包装器,如@State,将为您提供与对象相同的引用语义。

我知道这是一个旧答案,但是为了让大家知道,onChange 要求发布者是 Equatable,否则它将无法工作。 - Fernando Mata
1
这个回答与OP的问题没有任何关联。我理解OP的设置是为了简化他的真实问题,而真实问题通常要复杂得多。你的回答试图展示如何将过度简化应用于实际问题,这是一个注定会在实践中失败的尝试。 - CouchDeveloper
请停止试图在SwiftUI中教授MVVM,这是完全错误的。 - malhal
@CouchDeveloper 请阅读 https://azamsharp.com/2022/07/17/2022-swiftui-and-mvvm.html。 - malhal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接