SwiftUI 基于计算属性显示警报

Posted

技术标签:

【中文标题】SwiftUI 基于计算属性显示警报【英文标题】:SwiftUI show alert based on computed property 【发布时间】:2020-02-23 00:09:23 【问题描述】:

我正在尝试基于计算属性在 Swift 中显示警报。基本上,每当用户单击按钮时,“round”的值都会更新。当进行超过 10 轮时,会显示警报。

为此,我创建了一个名为“showingAlert”的布尔变量。这必须是一个 @State 变量,以便在用户关闭警报时再次设置为 false。

但是,编译器告诉我像@State 这样的属性包装“不能应用于计算属性”:-(

这是我尝试过的代码:


@State var round = 0
@State var showingAlert:Bool round > 10 ? true : false

func result(player: Int, app: Int) 
if player > app 
 round += 1
 
else 
 round += 1



var body: some View 
        Button(action: self.result(player: 1, app: 1)) 
        Text("Button")
        
           .alert(isPresented: $showingAlert) 
                Alert(title: Text("title"), message: Text("message"), dismissButton: .default(Text("Continue"))
                )
 

有没有办法解决这个问题?我很想创建一个不显示错误消息的警报。

【问题讨论】:

你不能在 if/else 之后直接添加round > 10 ?... 代码吗? showingAlert = round > 10 ? true : false。顺便说一句,你的 if 和 else 正在做同样的事情 【参考方案1】:

我更喜欢将逻辑放入模型中 - 将逻辑与视图分离 - 但这里有一些可行的方法:

@State var round = 0
@State var showingAlert:Bool = false

func result(player: Int, app: Int) 
    if player > app 
        round += 1
     else 
        round += 1
    
    if round > 10 
        showingAlert.toggle()
    

基本上,将您的支票移入您的功能。备注:

我假设这是测试逻辑...如果不是,您的 if/else 中有错字,因为它们都做同样的事情。 仅将 showingAlert 设置为 true - 让 SwiftUI 在解除警报时将其设置为 false。 将此逻辑与视图分开的真正原因是您可以使事情变得容易重置round。这是执行此操作的代码:
import SwiftUI
import Combine

class Model : ObservableObject 
    var objectWillChange = PassthroughSubject<Void, Never>()
    @Published var showingAlert = false 
        willSet 
            objectWillChange.send()
            if newValue == false 
                round = 0
            
        
    
    var round = 0
    func result(player: Int, app: Int) 
        if player > app 
            round += 1
         else 
            round += 1
        
        if round > 10 
            showingAlert.toggle()
        
    


struct ContentView: View 
    @EnvironmentObject var model: Model

    var body: some View 
        Button(action: self.model.result(player: 1, app: 1)) 
            Text("Button")
            
        .alert(isPresented: self.$model.showingAlert) 
                Alert(title: Text("title"), message: Text("message"), dismissButton: .default(Text("Continue")))
            
    

请注意,只有一个变量 (showingAlert) 标记为 @Published,您可以针对 willSet 正确编码,而您需要在 ContentView 中更改的所有内容是添加 EnvironmentObjectafter your add it to yourSceneDelegate`正确。

第一组代码将在第 11 次点击后显示警报,然后每次点击。第二组代码将在第 11 次点击后显示警报,之后每 11 次点击。

【讨论】:

为什么模型对象应该有一个属性来确定是否应该显示警报,这会在模型和控制器层之间创建不必要的依赖关系。如果你想为此使用模型对象,那么它应该发布round 属性。或者至少将其重命名为符合逻辑的名称,例如 gameIsOver @JoakimDanielson,我想这是风格问题。 (不过我明白你的意思。)但不是警报,让我们让它成为一个全屏模式 - 并发生后台进程。也许模型需要知道视图发生了什么。我想问题是什么驱动 UI - 用户交互和模型?还是 UI 驱动模型? 我不认为模型应该驱动任何东西或知道控制器或视图层发生了什么,它应该只是保持一个状态。然后由控制器层来响应该状态的变化或查询模型对象,因为一些内部请求或来自视图的请求。请注意,我认为您的回答没有任何问题,这更像是一条评论。【参考方案2】:

您可以简单地使用Binding.constant(_:)。将计算属性转换为绑定属性。


@State var round = 0
var showingAlert:Bool round > 10 ? true : false

func result(player: Int, app: Int) 
  if player > app 
   round += 1
   
  else 
   round += 1
  


var body: some View 
        Button(action: self.result(player: 1, app: 1)) 
        Text("Button")
        
           .alert(isPresented: .constant(showingAlert)) 
                Alert(title: Text("title"), message: Text("message"), dismissButton: .default(Text("Continue"))
                )
 

【讨论】:

如果我像.alert(isPresented: .constant(showingAlert))这样使用,则无法解除警报 是和否,两者兼而有之。是的,因为您不能直接切换显示警报变量,因为它是计算属性,但如果您想切换,您可以简单地更改依赖状态变量,使其切换计算值,在这种情况下为 round

以上是关于SwiftUI 基于计算属性显示警报的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - @Binding 到访问 ObservableObject 属性内的值的计算属性会复制变量吗?

为 Swiftui 视图转换 @State 的计算属性

在 SwiftUI 视图中设置计算属性无法编译

将 SwiftUI 警报或操作表绑定到值类型模型属性的可选性(当属性为 nil 时显示视图)的好方法是啥?

SwiftUI 中子结构的计算属性未更新

基于外部单例类中的属性的 SwiftUI 显示表