SwiftUI @Binding在带有不同导航项的push / pop上重新加载

时间:2020-06-23 10:01:50

标签: mvvm swiftui

我有一个非常简单的应用程序示例,该示例具有两个视图:MasterView和DetailView。 MasterView显示在ContentView内部,并带有NavigationView:

import SwiftUI

struct ContentView: View {

    var body: some View {
        NavigationView {
            MasterView(viewModel: MasterViewModel())
                .navigationBarTitle(Text("Master"))
                .navigationBarItems(
                    leading: EditButton()
                )
        }
    }
}

struct MasterView: View {

    @ObservedObject private var viewModel: MasterViewModel

    init(viewModel: MasterViewModel) {
        self.viewModel = viewModel
    }

    var body: some View {
        print("Test")
        return DataStatusView(dataSource: self.$viewModel.result) { texts -> AnyView in
            print("Closure")
            return AnyView(List {
                ForEach(texts, id: \.self) { text in
                    NavigationLink(
                        destination: DetailView(viewModel: DetailViewModel(stringToDisplay: text))
                    ) {
                        Text(text)
                    }
                }
            })
        }.onAppear {
            if case .waiting = self.viewModel.result {
                self.viewModel.fetch()
            }
        }
    }
}

struct DetailView: View {
    @ObservedObject private var viewModel: DetailViewModel


    init(viewModel: DetailViewModel) {
        self.viewModel = viewModel
    }

    var body: some View {
        self.showView().onAppear {
            self.viewModel.fetch()
        }
        .navigationBarTitle(Text("Detail"))
    }

    func showView() -> some View {
        switch self.viewModel.result {
        case .found(let s):
            return AnyView(Text(s))
        default:
            return AnyView(Color.red)
        }
    }
}

DataStatusView是管理某些状态的简单视图:


public enum ResultState<T, E: Error> {
    case waiting
    case loading
    case found(T)
    case failed(E)
}
struct DataStatusView<Content, T>: View where Content: View {

    @Binding private(set) var dataSource: ResultState<T, Error>
    private let content: (T) -> Content
    private let waitingContent: AnyView?

    @inlinable init(dataSource: Binding<ResultState<T, Error>>,
                    waitingContent: AnyView? = nil,
                    @ViewBuilder content: @escaping (T) -> Content) {
        self._dataSource = dataSource
        self.waitingContent = waitingContent
        self.content = content
    }

    var body: some View {
        self.buildMainView()
    }

    private func buildMainView() -> some View {
        switch self.dataSource {
        case .waiting:
            return AnyView(Color.red)
        case .loading:
            return AnyView(Color.green)
        case .found(let data):
            return AnyView(self.content(data))
        case .failed:
            return AnyView(Color.yellow)
        }
    }
}

和视图模型是一个非常简单的“假装进行网络调用”的虚拟机:

final class MasterViewModel: ObservableObject {

    @Published var result: ResultState<[String], Error> = .waiting

    init() { }

    func fetch() {
        self.result = .loading
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
            guard let self = self else { return }
            self.result = .found(["This", "is", "a", "test"])
        }
    }
}

final class DetailViewModel: ObservableObject {

    @Published var result: ResultState<String, Error> = .waiting

    private let stringToDisplay: String
    init(stringToDisplay: String) {
        self.stringToDisplay = stringToDisplay
    }

    func fetch() {
        self.result = .loading
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
            guard let self = self else { return }
            self.result = .found(self.stringToDisplay)
        }
    }
}

现在我遇到的问题是,每次我从Master-> Detail视图进入时,都会调用DataStatusView内部的块。这是一个问题,因为将不断重新创建“ DetailView”(因此也将重新创建它的vm,这将导致无法加载明细数据)。

之所以发生这种情况,是因为当我从master转到-> detail时,导航栏中的按钮会更改(或者至少是假设)。当我删除行时:

.navigationBarItems(
                    leading: EditButton()
                )

这是“预期”。

处理此问题的“ SwiftUI”方法是什么?一个显示此问题的示例项目在这里:https://github.com/kerrmarin/swiftui-mvvm-master-detail

0 个答案:

没有答案