是否可以创建可由多个ContentView使用的通用ObservableObject类?

时间:2019-09-24 20:17:39

标签: ios swift swiftui xcode11 combine

嗨,我只是想知道是否可以创建一个通用类,以确认ObservableObject协议,该协议可以被多个ContentView使用。

如果我能做到这一点,那么我将能够使ContentView和Model类完全通用且可重用。

我想要达到的目标的一个例子:

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

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

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

如果可以的话,任何类都可以实现ContentViewModelType并成为ContentView的模型,从而使其具有通用性和可重用性。例如

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

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

enter image description here

我认为引入一些关键字的全部目的是,以便我们可以将协议作为类型用于那些将类型关联为要求的协议,因此这应该可行。但这会出错。

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

谢谢。

3 个答案:

答案 0 :(得分:2)

这不是some的用途。 some创建一个不透明的返回类型,而不是一个存在(“ any”)类型。 See the docs for more detail.另请参见What is the `some` keyword in SwiftUI?

some类型必须是在编译时已知的单个类型。只是呼叫者不知道。您想要做的是传递一个存在的存在,这是 runtime 已知的类型。在Swift 5.1中,关于存在性的一切都没有改变。如果这是您想要的,您仍然需要将其包装在AnyContentViewModel中。 (我需要考虑一下这是否是一个好主意。)

但是您编写的代码也无法满足您的描述。您实际上并没有在任何地方使用ContentViewModelType。您是说some ContentViewModelType吗?那仍然行不通,但这似乎是您的意思。

答案 1 :(得分:1)

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

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($0)
            }
        }
    }
}

要使用此实例,您需要:

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

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

答案 2 :(得分:-1)

如果我对您的理解正确,则可以执行以下操作:

您可以具有一个ObservableObject类,如下所示:

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

ContentView:

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()函数中对其进行了初始化,然后在{{1}中创建了SampleTimer()的新实例}(即SecondView而不是我拥有的)而不是将同一实例从@ObservedObject var sampleTimer = SampleTimer()传递到ContentView(),您将拥有SecondView()的两个不同实例SampleTimerContentView

中的值将不同