SwiftUI-子类化的viewModel不会触发视图刷新

时间:2020-09-12 20:11:26

标签: swiftui

在这种情况下,我有一个BaseView包含一些常见元素,一个BaseViewModel包含一些常见功能,但是当其@Published var得到更新时,没有BaseView刷新发生。

设置是这样的:

class BaseViewModel: ObservableObject {

    @Published var overlayView: AnyView = EmptyView().convertToAnyView()

    func forceViewRefresh() {
        self.objectWillChange.send()
    }

    func setOverlayView(overlayView: AnyView) {
        self.overlayView = overlayView
    }

}

此视图模型子类为BaseViewModel

class FirstViewModel: BaseViewModel {
    func showOverlayView() {
        self.setOverlayView(overlayView: OverlayView().convertToAnyView())
    }
}

我还有一个BaseView,我在其中使用overlayView

struct BaseView<Content: View>: View {

let content: Content
@ObservedObject var viewModel = BaseViewModel()

init(content: () -> Content) {
    self.content = content()
}

var body: some View {
    
    ZStack {
        Color.green.edgesIgnoringSafeArea(.vertical)
        content
        viewModel.overlayView
    }
    
}

}

显示的第一个视图是FirstView,它符合BaseViewProtocol,并且具有扩展FirstViewModel的{​​{1}}。

BaseViewModel

}

单击struct FirstView: BaseViewProtocol { @ObservedObject var viewModel = FirstViewModel() var body: some View { BaseView() { Button("Show overlay") { viewModel.showOverlayView() } } } 中的Show overlay按钮将调用First View上的showOverlayView()函数,该功能继而调用FirstViewModel上的setOverlayViewBaseViewModeloverlayView的值按预期变化,但未调用BaseViewModel上的视图刷新。 我在做什么错了?

非常感谢。

2 个答案:

答案 0 :(得分:0)

我刚刚测试了此代码示例,并且可以在Xcode 12 beta 6和iOS 14 beta 8上正常工作

struct ContentView: View {
    
    @StateObject private var viewModel = FirstViewModel()
    
    var body: some View {
        ZStack {
            Button(action: { viewModel.showOverlayView() }) {
                Text("Press")
            }
            viewModel.overlayView
        }
    }
}

class BaseViewModel: ObservableObject {
    
    @Published var overlayView: AnyView = AnyView(EmptyView())
    
    func forceViewRefresh() {
        self.objectWillChange.send()
    }
    
    func setOverlayView(overlayView: AnyView) {
        self.overlayView = overlayView
    }
    
}

class FirstViewModel: BaseViewModel {
    func showOverlayView() {
        self.setOverlayView(
            overlayView: AnyView(
                Color.blue
                    .opacity(0.2)
                    .allowsHitTesting(false)
            )
        )
    }
}

答案 1 :(得分:0)

通常在SwiftUI中,您不会在body之外创建视图。视图创建应该留给SwiftUI-相反,您可以定义一些其他控件来告诉SwiftUI 如何何时创建视图。

这是一个简化的演示,如何为不同的视图显示不同的叠加层。

您可以创建基本的OverlayView:

enum OverlayType {
    case overlay1, overlay2
}

struct OverlayView: View {
    let overlayType: OverlayType

    @ViewBuilder
    var body: some View {
        if overlayType == .overlay1 {
            Text("Overlay1") // can be replaced with any view you want
        }
        if overlayType == .overlay2 {
            Text("Overlay1")
        }
    }
}

并在您的BaseView中使用它(如果overlayTypenil,则不会显示叠加层)

struct BaseView<Content>: View where Content: View {
    let overlayType: OverlayType?
    let content: () -> Content

    var body: some View {
        ZStack {
            Color.green.edgesIgnoringSafeArea(.vertical)
            content()
            if overlayType != nil {
                OverlayView(overlayType: overlayType!)
            }
        }
    }
}

现在在ContentView中,您可以使用BaseView并指定其OverlayType。

struct ContentView: View {
    @State var overlayType: OverlayType?

    var body: some View {
        BaseView(overlayType: overlayType) {
            Button("Show overlay") {
                overlayType = .overlay1
            }
        }
    }
}

一些注意事项:

  • 为简单起见,我使用了@State变量来控制叠加层。如果您的ViewModel还有其他用例,则可能需要将逻辑移到那里。

  • 请注意,最好使用AnyView代替@ViewBuilder

  • 此外,如果您想在视图内观察ObservableObject,则需要使用@ObservedObject,而不是@ObservableObject