【问题标题】:Can I create a generic ObservableObject class which can be used by multiple ContentViews?我可以创建一个可供多个 ContentView 使用的通用 ObservableObject 类吗?
【发布时间】:2020-01-25 00:09:19
【问题描述】:

您好,我只是想知道是否可以创建一个通用类来确认 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 给了我一个类型错误。

我认为引入一些关键字的全部目的是为了让我们可以使用协议作为类型来处理那些需要关联类型的协议,因此这应该可以工作。但它给出了一个错误。

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

提前致谢。

【问题讨论】:

  • protocol ContentViewModelType: ObservableObject... 纠正我,但你不能这样做 - ObservableObject总是class 对象。
  • dfd 一个协议可以继承另一个协议。它只是意味着“这个协议需要我祖先的所有东西,而且……”你说得对,ContentViewModelType符合ObservableObject。不幸的是,“继承自”和“符合”在 Swift 中拼写相同。
  • 这项技术在 WWDC 2020 Structure your app for SwiftUI previews 中得到演示,从 21:13 developer.apple.com/videos/play/wwdc2020/10149/?time=1273

标签: ios swift swiftui xcode11 combine


【解决方案1】:

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

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

但是您编写的代码也没有按照您的描述进行。您实际上并没有在任何地方使用ContentViewModelType。你的意思是some ContentViewModelType?这仍然行不通,但似乎是你的意思。

【讨论】:

    【解决方案2】:

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

    你可以有一个 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
                }
            }
        }
    }
    

    一个内容视图:

    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 中创建SampleTimer() 的新实例(即@ObservedObject var sampleTimer = SampleTimer()而不是我所拥有的)而不是将相同的实例从ContentView() 传递到SecondView(),您将拥有SampleTimer 的两个不同实例,这意味着ContentViewSecondView

    【讨论】:

      【解决方案3】:

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

      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 用于不透明类型,它不会使您的代码通用。

      【讨论】:

      • 这正是我想要的。太感谢了。为什么我没有想到将视图结构设为通用。
      • 很高兴你发现它很有用。
      • 救命答案!准点!
      • 如果 ContentView 有 init() 方法,这似乎不起作用,你能帮忙吗?
      猜你喜欢
      • 2020-07-11
      • 1970-01-01
      • 2016-05-22
      • 1970-01-01
      • 2020-01-10
      • 1970-01-01
      • 2012-05-15
      • 2011-04-20
      • 1970-01-01
      相关资源
      最近更新 更多