从视图模型 [MODAL PAGE] 中关闭视图

Dismiss view from view model [MODAL PAGE]

我正在使用 swiftUI 和组合,我的 VM 中有一些业务逻辑。有些结果不得不否定我的观点。

我在某些视图中使用了这个:

@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

self.presentationMode.wrappedValue.dismiss()

我想在我的视图模型中使用类似的东西。

您没有在 SwiftUI 中以命令方式执行解雇。相反,您可以通过将 .sheet 视图绑定到一个布尔值 属性 来使用该视图,该视图模型将从该视图模型中发生变异。

编辑:

After answering a follow-up , I came up with a different approach. It plays nice if the dismissal is actually needed to be done from inside the modally presented View itself.

您可以通过实施您的自定义 Publisher 来实现此目的,该自定义 Publisher 将使用 .send() 方法允许您向订阅者发送特定值(在本例中为您的 View)。您将使用在 SwiftUIView 协议上定义的 onReceive(_:perform:) 方法来订阅您定义的自定义 Publisher 的输出流。在 perform 动作闭包中,您可以访问发布者的最新发射值,您将实际解雇 View

理论讲完了,你可以看看代码,应该不会很难理解,如下:

import Foundation
import Combine

class ViewModel: ObservableObject {
    var viewDismissalModePublisher = PassthroughSubject<Bool, Never>()
    private var shouldDismissView = false {
        didSet {
            viewDismissalModePublisher.send(shouldDismissView)
        }
    }

    func performBusinessLogic() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            self.shouldDismissView = true
        }
    }
}

而同行的观点是:

import SwiftUI

struct ContentView: View {
    @State private var isDetailShown = false
    var body: some View {
        VStack {
            Text("Hello, World!")
            Button(action: {
                self.isDetailShown.toggle()
            }) {
                Text("Present Detail")
            }
        }
        .sheet(isPresented: $isDetailShown) {
            DetailView()
        }
    }
}

struct DetailView: View {
    @ObservedObject var viewModel = ViewModel()
    @Environment(\.presentationMode) private var presentationMode
    var body: some View {
        Text("Detail")
        .navigationBarTitle("Detail", displayMode: .inline)
        .onAppear {
            self.viewModel.performBusinessLogic()
        }
        .onReceive(viewModel.viewDismissalModePublisher) { shouldDismiss in
            if shouldDismiss {
                self.presentationMode.wrappedValue.dismiss()
            }
        }
    }
}

旧答案:

关于视图模型中业务逻辑更改的视图关闭的一个非常简单的实现是:

struct ContentView: View {
    @ObservedObject var viewModel = ViewModel()
    var body: some View {
        Text("Hello, World!")

        // the animation() modifier is optional here
        .sheet(isPresented: $viewModel.isSheetShown.animation()) { 
            Text("Sheet Presented")
        }

        // From here - for illustration purpose
        .onAppear {
            self.viewModel.perform()
        }
        // To here

    }
}

class ViewModel: ObservableObject {
    @Published var isSheetShown = false

    func perform() {
        // this just an example. In real application, you will be responsible to
        // toggle between the states of the `Bool` property
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            self.isSheetShown.toggle()
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                self.isSheetShown.toggle()
            }
        }
    }
}

如果你想让它变得简单,那么只需在 viewModel 中创建一个名为 goBack 的 Bool 类型的 @Published 变量,并在需要时将其更改为 true 并且在视图中只需使用 .onChange 修饰符,如果该 bool 为真然后更改 运行 presentationMode.wrappedValue.dismiss().

class ViewModel: ObservableObject {
  @Published var goBack: Bool = false
  
  fun itWillToggleGoBack() {
    goBack.toggle()
  }
}


struct MyView {
  @StateObject var vm = ViewModel()
  @Environment(\.presentationMode) var presentationMode

  var body: some View {
    Text("Any kind of view")
      .onChange(of: vm.goBack) { goBack in 
         if goBack {
           self.presentationMode.wrappedValue.dismiss()
         }
      }
  }
}