使用 MVVM 在 SwiftUI 中显示警报
Posted
技术标签:
【中文标题】使用 MVVM 在 SwiftUI 中显示警报【英文标题】:Presenting an Alert in SwiftUI using MVVM 【发布时间】:2020-02-06 19:45:29 【问题描述】:我正在尝试使用 SwiftUI 和 MVVM 架构构建应用程序。我想让我的 View 在它的 ViewModel 认为有必要时显示一个警报——比如,当它有一个来自 Model 的某种新结果时。因此,假设每当 VM 检测到新结果时,它都会相应地设置其 status
:
ViewModel:
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 演示的精神,但我找不到方法……
【问题讨论】:
是否有任何文档参考或解释说明为什么这不起作用?我似乎无法理解这背后的原因。 @Rikh 只要将announcingResult
设置为true
,它就可以工作。但我的目标是在vm.status
变为某个值时发出警报……而上面的代码没有任何规定可以做到这一点。这就是为什么@asperi 在下面的回答是必要的。
啊,我看错了你上面的例子。道歉。我发现在视图模型中直接使用@Published
值来控制警报也不会导致它出现在View
中。所以一直在寻找答案!
【参考方案1】:
这是可能的方法(经过测试并适用于 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")))
【讨论】:
谢谢你,@Asperi!这看起来棒极了。我会试一试。我没有想到我可能必须进行手动绑定……到目前为止我从来没有这样做过,但这似乎是一个优雅的解决方案。如果有一个可以避免它们,我会很高兴;如果没有,我认为这会起作用! 正如@user12208004 指出的那样,我对绑定的覆盖在 Xcode 11.5 中不再起作用。这种行为似乎被打破了。 我没有 Xcode 11.5。使用 Xcode 12 重新测试 - 工作正常。确保在主队列上修改视图模型发布的属性。 我更新到 Xcode 11.6,它也可以正常工作。谢谢@Asperi【参考方案2】:我创建了一个辅助类
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
在VM中可以这样使用
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"
)
我们还可以使用警报视图类的扩展
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)
我相信这种方法可以进一步改进
【讨论】:
您好,感谢您的回答。您是否不需要将来自 AlertProvider 类 (shouldShowAlert) 的更改从 VM 内部发送到您的视图,然后才能正常工作,还是我遗漏了什么?以上是关于使用 MVVM 在 SwiftUI 中显示警报的主要内容,如果未能解决你的问题,请参考以下文章