我可以创建一个可以被多个 ContentView 使用的通用 ObservableObject class 吗?

Can I create a generic ObservableObject class which can be used by multiple ContentViews?

你好,我只是想知道是否可以创建一个通用的 class 确认 ObservableObject 协议,它可以被多个 ContentView 使用。

如果我能做到,那么我就能使我的 ContentView 和模型 class 完全通用且可重用。

我想要实现的一个例子:

protocol ContentViewModelType: ObservableObject {
    var propertyToInitialiseView: [String] { get }
}

struct ContentView: View {
    @ObservedObject var viewModel: some ViewModel

    var body: some View {
        Text("Hello World")
    }
}

如果我能做到这一点,任何 class 都可以实现 ContentViewModelType 并成为 ContentView 的模型,使其通用且可重用。例如

class ViewModel: ObservableObject {
    var objectWillChange = PassthroughSubject<ViewModel, Never>()
}

但是当我尝试初始化 ContentView 时 xcode 给我一个类型错误。

我认为引入一些关键字的全部意义在于,我们可以将协议用作那些需要关联类型的协议的类型,因此这应该可行。但是它给出了一个错误。

如果有人对此问题有任何参考资料或知识可以分享,或者可能有解决方案,那就太好了。

提前致谢。

这不是 some 的目的。 some 创建一个不透明的 return 类型,而不是存在的 ("any") 类型。 See the docs for more detail. See also

some 类型必须是编译时已知的单一类型。只是调用者不知道。您要做的是传递一个存在性,这是 运行时 已知的类型。 Swift 5.1 中关于存在主义的内容没有任何变化。如果这是您想要的,您仍然需要将其包装在 AnyContentViewModel 中。 (我需要考虑一下这是否是个好主意。)

但是您编写的代码也没有按照您的描述进行操作。您实际上并没有在任何地方使用 ContentViewModelType 。您是说 some ContentViewModelType 吗?还是不行,不过好像是这个意思。

如果我对你的理解正确,你可以这样做:

你可以有一个 ObservableObject class 如下:

import Foundation

class SampleTimer: ObservableObject {
    @Published var timerVar: Int = 0

    init(){
        Timer.scheduledTimer(withTimeInterval: 1, repeats: true){
            timer in
            DispatchQueue.main.async {
                self.timerVar += 1
            }
        }
    }
}

一个内容视图:

import SwiftUI

struct ContentView: View {
    @ObservedObject var sampleTimer = SampleTimer()
    var body: some View {
        NavigationView {
            VStack {
                Text("\(sampleTimer.timerVar)")
                NavigationLink(destination: SecondView(sampleTimer: sampleTimer) ){
                    Text("Go to second view")
                }
            }
        }
    }
}

和像这样的 SecondView:

import SwiftUI

struct SecondView: View {
    @ObservedObject var sampleTimer : SampleTimer
    var body: some View {
        VStack {
            Text("\(sampleTimer.timerVar)")
        }
    }
}

我想概述的重要事情是,如果你有一个像我一样的计时器并且它在 init() 函数中初始化并且你在 SecondView (即 @ObservedObject var sampleTimer = SampleTimer() 而不是我所拥有的)而不是将相同的实例从 ContentView() 传递到 SecondView() 你将有两个不同的 SampleTimer 实例这意味着 ContentViewSecondView

中的值不会相同

试图理解您的问题,但我不太确定我理解其意图...但是要创建一个采用通用视图模型的视图(基于您之前的协议),您将需要以下代码:

protocol ViewModelWithProperties: ObservableObject {
    var properties: [String] { get }
}

struct SomeView<T>: View where T: ViewModelWithProperties {
// this can also be written as 
// struct SomeView<T: ViewModelWithProperties>: View {
    @ObservedObject var item: T

    var body: some View {
        VStack {
            ForEach(item.properties, id: \.self) {
                Text([=10=])
            }
        }
    }
}

要使用此实例,您需要:

struct ContentView: View {
    var body: some View {
        SomeView(item: MyViewModel())
    }
}

如其他答案之一所述,some 用于不透明类型,它不会使您的代码通用。