使用 MVVM 在 SwiftUI 中呈现警报
Presenting an Alert in SwiftUI using MVVM
我正在尝试使用 SwiftUI 和 MVVM 架构构建应用程序。我想让我的视图在它的 ViewModel 认为有必要时发出警报——比如,当它从模型中获得某种可用的新结果时。因此,假设每当 VM 检测到新结果时,它都会相应地设置其 status
:
视图模型:
enum Status {
case idle
case computing
case newResultAvailable
}
class MyViewModel: ObservableObject {
@Published var status = Status.idle
...
}
视图:
struct ContentView: View {
@ObservedObject var vm = MyViewModel()
@State private var announcingResult = false {
didSet {
// reset VM status when alert is dismissed
if announcingResult == false {
vm.status = .idle
}
}
}
var body: some View {
Text("Hello")
.alert(isPresented: $announcingResult) {
Alert(title: Text("There's a new result!"),
message: nil,
dismissButton: .default(Text("OK")))
}
}
}
Apple 已将 .alert()
修饰符设计为将绑定作为其第一个参数,以便在绑定 属性 变为 true
时显示警报。然后,当警报解除时,绑定 属性 会自动设置为 false
.
我的问题是:
当 VM 的 status
变为 .newResultAvailable
时,如何让警报出现?在我看来,这就是 MVVM 应该如何发挥作用,而且感觉非常符合所有 SwiftUI WWDC 演示的精神,但我找不到办法……
这是可能的方法(已测试并适用于 Xcode 11.3+)
struct ContentView: View {
@ObservedObject var vm = MyViewModel()
var body: some View {
let announcingResult = Binding<Bool>(
get: { self.vm.status == .newResultAvailable },
set: { _ in self.vm.status = .idle }
)
return Text("Hello")
.alert(isPresented: announcingResult) {
Alert(title: Text("There's a new result!"),
message: nil,
dismissButton: .default(Text("OK")))
}
}
}
有时也可以使用以下表示法
var body: some View {
Text("Hello")
.alert(isPresented: Binding<Bool>(
get: { self.vm.status == .newResultAvailable },
set: { _ in self.vm.status = .idle }
)) {
Alert(title: Text("There's a new result!"),
message: nil,
dismissButton: .default(Text("OK")))
}
}
我创建了一个助手class
class AlertProvider {
struct Alert {
var title: String
let message: String
let primaryButtomText: String
let primaryButtonAction: () -> Void
let secondaryButtonText: String
}
@Published var shouldShowAlert = false
var alert: Alert? = nil { didSet { shouldShowAlert = alert != nil } }
}
在虚拟机中可以这样使用
var alertProvider = AlertProvider()
func showAlert() {
alertProvider.alert = AlertProvider.Alert(
title: "The Locatoin Services are disabled",
message: "Do you want to turn location on?",
primaryButtomText: "Go To Settings",
primaryButtonAction: { [weak self] in
self?.appSettingsHandler.openAppSettings()
},
secondaryButtonText: "Cancel"
)
}
我们还可以使用警报视图的扩展 class
extension Alert {
init(_ alert: AlertProvider.Alert) {
self.init(title: Text(alert.title),
message: Text(alert.message),
primaryButton: .default(Text(alert.primaryButtomText),
action: alert.primaryButtonAction),
secondaryButton: .cancel(Text(alert.secondaryButtonText)))
}
}
然后View会按照下面的方式使用它
.alert(isPresented: $viewModel.alertProvider.shouldShowAlert ) {
guard let alert = viewModel.alertProvider.alert else { fatalError("Alert not available") }
return Alert(alert)
}
我相信这种方法可以进一步改进
我正在尝试使用 SwiftUI 和 MVVM 架构构建应用程序。我想让我的视图在它的 ViewModel 认为有必要时发出警报——比如,当它从模型中获得某种可用的新结果时。因此,假设每当 VM 检测到新结果时,它都会相应地设置其 status
:
视图模型:
enum Status {
case idle
case computing
case newResultAvailable
}
class MyViewModel: ObservableObject {
@Published var status = Status.idle
...
}
视图:
struct ContentView: View {
@ObservedObject var vm = MyViewModel()
@State private var announcingResult = false {
didSet {
// reset VM status when alert is dismissed
if announcingResult == false {
vm.status = .idle
}
}
}
var body: some View {
Text("Hello")
.alert(isPresented: $announcingResult) {
Alert(title: Text("There's a new result!"),
message: nil,
dismissButton: .default(Text("OK")))
}
}
}
Apple 已将 .alert()
修饰符设计为将绑定作为其第一个参数,以便在绑定 属性 变为 true
时显示警报。然后,当警报解除时,绑定 属性 会自动设置为 false
.
我的问题是:
当 VM 的 status
变为 .newResultAvailable
时,如何让警报出现?在我看来,这就是 MVVM 应该如何发挥作用,而且感觉非常符合所有 SwiftUI WWDC 演示的精神,但我找不到办法……
这是可能的方法(已测试并适用于 Xcode 11.3+)
struct ContentView: View {
@ObservedObject var vm = MyViewModel()
var body: some View {
let announcingResult = Binding<Bool>(
get: { self.vm.status == .newResultAvailable },
set: { _ in self.vm.status = .idle }
)
return Text("Hello")
.alert(isPresented: announcingResult) {
Alert(title: Text("There's a new result!"),
message: nil,
dismissButton: .default(Text("OK")))
}
}
}
有时也可以使用以下表示法
var body: some View {
Text("Hello")
.alert(isPresented: Binding<Bool>(
get: { self.vm.status == .newResultAvailable },
set: { _ in self.vm.status = .idle }
)) {
Alert(title: Text("There's a new result!"),
message: nil,
dismissButton: .default(Text("OK")))
}
}
我创建了一个助手class
class AlertProvider {
struct Alert {
var title: String
let message: String
let primaryButtomText: String
let primaryButtonAction: () -> Void
let secondaryButtonText: String
}
@Published var shouldShowAlert = false
var alert: Alert? = nil { didSet { shouldShowAlert = alert != nil } }
}
在虚拟机中可以这样使用
var alertProvider = AlertProvider()
func showAlert() {
alertProvider.alert = AlertProvider.Alert(
title: "The Locatoin Services are disabled",
message: "Do you want to turn location on?",
primaryButtomText: "Go To Settings",
primaryButtonAction: { [weak self] in
self?.appSettingsHandler.openAppSettings()
},
secondaryButtonText: "Cancel"
)
}
我们还可以使用警报视图的扩展 class
extension Alert {
init(_ alert: AlertProvider.Alert) {
self.init(title: Text(alert.title),
message: Text(alert.message),
primaryButton: .default(Text(alert.primaryButtomText),
action: alert.primaryButtonAction),
secondaryButton: .cancel(Text(alert.secondaryButtonText)))
}
}
然后View会按照下面的方式使用它
.alert(isPresented: $viewModel.alertProvider.shouldShowAlert ) {
guard let alert = viewModel.alertProvider.alert else { fatalError("Alert not available") }
return Alert(alert)
}
我相信这种方法可以进一步改进