SwiftUI: Is there a way to assign a View to a variable for later use of the same instance?

84 views Asked by At

I have a parent View:

struct ParentView<pViewModel> : View where pViewModel: ParentViewModel {
    var body: some View {
        VStack {
             ChildView(viewModel: ViewModel(list:["a", "b", "c"]))
             ChildView(viewModel: ViewModel(list:["x", "y", "z"]))
        }
    }
}

In my child view, I have the following:

struct ChildView<cViewModel>: View where cViewModel: ChildViewModel {
    @ObservedObject var vm: cViewModel

    var body: some View {
         ...
    }
}

and this is the ViewModel of the child view:

protocol ChildViewModel: ObservableObject { ... }

class ChildViewModelImp: ChildViewModel {
     @Published var toggles: [String: Bool] = [:]
     var list: [String] = []

     init(list: [String]) {
        self.list = list

        for item in list {
            toggles[item] = false
        }
     }

     func toggleItem(item: String) {
          toggles[item].toggle()
     }
}

When an item in toggles gets toggled (via func toggleItem), both the parent view and the child view get re-rendered. The parent view creates a new instance of the child view so the viewModel init() is called again and I lose the data in the toggles property.

I tried saving the two child views into two properties in the parent View model, and call this in my Parent View like so:

struct ParentView<pViewModel> : View where pViewModel: ParentViewModel {

      @StateObject var vm: pViewModel

      var body: some View {
            vm.displayChildView(list: [...], isTop: true)
            vm.displayChildView(list: [...], isTop: false)
      }
}

protocol ParentViewModel: ObservableObject {
    associatedtype childVM: ChildViewModel
    func displayChildView(list: [String]) -> ChildView<childVM>
}

class ParentViewModelImp: ParentViewModel {
     private var childViewTop: ChildView<some ChildViewModel>?
     private var childViewBottom: ChildView<some ChildViewModel>?

     func displayChildView(list: [String], isTop: Bool) -> ChildView<some ChildViewModel> {
          if isTop {
             if childViewTop == nil {
                childViewTop = ChildView(viewModel: getChildViewModel(list: list))
             } else {
                return childViewTop
             }
          } else {
             if childViewBottom == nil {
                childViewBottom = ChildView(viewModel: getChildViewModel(list: list))
             } else {
                return childViewBottom
             }
         }
      }
      
      private func getChildViewModel(list: [String]) -> some ChildViewModel {
          var viewModel = ChildViewModelImp(list:list)
          ...
          return viewModel
      }
}

However it seems like the childViewTop and childViewBottom have different type from what getChildViewModel() returns.

Because of "some" keyword, it seems like my types do not match. How can I save the child views in variables so that I can reuse the same instance of the child views when it gets re-rendered and I don't lose my data in the child view model?

0

There are 0 answers