单击swiftui中的按钮后如何根据条件显示不同的警报

vks*_*bvk 4 ios swift swiftui

我在这里发布之前做了一些研究,但我无法修复它。

在注册视图中,我希望用户注册。

我创建了一个链表,当用户注册用户名时,我的程序会检查用户名是否已被占用。

如果被占用,它应该发出警报,说明用户名已被占用,因为用户点击了注册按钮。

如果用户名没有被占用,那么它应该显示一个警报,说注册成功

import SwiftUI

struct registerScreen: View {

    @State var username: String = ""
    @State var password: String = ""
    @State private var sucessfulRegister = false
    @State private var failedRegister = false

    var body: some View {

        VStack {

            TextField()
            SecureField()
            
            Button(action: {

                let userinfo = linkedList()

                if (userinfo.contains(value: self.username)){
                    // self.failedRegister = true
                    self.failedRegister.toggle()
                    // show alert that it failed

                } else {
                    userinfo.insert(value: user(username: self.username, password: self.password))
                    // show alert that it is successfull
                    self.sucessfulRegister.toggle()

                }
            })

            {

                Text("Register")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .frame(width: 220, height: 60)
                    .background(Color.green)
                    .cornerRadius(15.0)

            }

        }

    }

}
Run Code Online (Sandbox Code Playgroud)

And*_*rew 8

这是可能的。尽管您不需要跟踪尽可能多的状态。

首先,您只需要跟踪它们是否失败。因此,您failedRegister将跟踪用户是否已成功注册。这意味着我们可以删除successfulRegister.

我们需要一个变量来跟踪警报是否显示,为此我们将使用变量 showAlert

由于您有一个提供用户信息的链表,我们将仅使用一个包含几个用户名的数组来模拟它。

所以这里是你的代码的简化版本,应该可以工作。

struct ContentView: View {
    
    var names: [String] = ["John", "Mike"]
    
    @State var username: String = ""
    @State var password : String = ""
    @State private var failedRegister = false
    
    // this value is used for tracking whether the alert should be shown
    @State private var showAlert = false
    
    var body: some View {
        VStack {
            TextField("Enter username", text: $username)

            Button(action: {
                // reset to false as this is the initial state
                self.failedRegister = false
                
                if (self.names.contains(self.username)){
                    self.failedRegister.toggle()
                } else {
                    // insert the value into the user info
                }
                self.showAlert.toggle()

            }) {
                Text("Register")
                    .font(.headline)
                    .foregroundColor(.white)
                    .padding()
                    .frame(width: 220, height: 60)
                    .background(Color.green)
                    .cornerRadius(15.0)
            }
            
        }.alert(isPresented: $showAlert) {
            // it would be nice to set failedRegister back to false in this function but you cannot modify state here.
            if self.failedRegister {
                return  Alert(title: Text("Failed to register"), message: Text("Unfortunately that username is taken"), dismissButton: .default(Text("OK")))
            } else {
                return  Alert(title: Text("Welcome"), message: Text("You have registered"), dismissButton: .default(Text("OK")))
            }
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

使用可识别更新

有一种替代方法可以Alerts在同一个View. 这是使用对Identifiable.

如果我们看看我们可以Alert在 a 上初始化 an 的方法,我们会View发现有两种方法。第一个具有以下签名:

.alert(isPresented: Binding<Bool>, content: () -> Alert)
Run Code Online (Sandbox Code Playgroud)

这就是上面示例中使用的内容。

但是,还有第二种方式具有以下签名:

.alert(item: Binding<Identifiable?>, content: (Identifiable) -> Alert)
Run Code Online (Sandbox Code Playgroud)

第二种方式可以允许管理更复杂的警报。为了利用这一点,我们需要一些东西来跟踪警报的状态。我们可以创建一个简单的结构,它符合Identifiable并包含我们对警报的不同选择的枚举。

然后我们创建一个@State变量来跟踪AlertIdentifier和初始化nil,使其状态为空,并且在更改之前不会显示任何警报。

然后我们可以将我们的添加.alert(item:content:)到我们的View.

这是一个简单的例子,展示了它的实际效果。

struct ContentView:View {

    private struct AlertIdentifier: Identifiable {
        var id: Choice

        enum Choice {
            case success
            case failure
        }
    }

    @State private var showAlert: AlertIdentifier? // init this as nil

    var body: some View {
        VStack(spacing: 20) {
            Button(action: {
                self.showAlert = AlertIdentifier(id: .success)

            }, label: {
                Text("Show success alert")
            })

            Button(action: {
                self.showAlert = AlertIdentifier(id: .failure)

            }, label: {
                Text("Show failure alert")
            })
        }
        .alert(item: $showAlert) { alert -> Alert in

            switch alert.id {
            case .success:
                return Alert(title: Text("Success"), message: Text("You have successfully registered"), dismissButton: .default(Text("OK")))

            case .failure:
               return Alert(title: Text("Failure"), message: Text("You have failed to register"), dismissButton: .default(Text("OK")))
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,在按钮中,我们将 设置为具有我们想要显示的警报类型showAlert的结构实例AlertIdentifier。在这种情况下,我们有两种类型:成功和失败(但我们可以拥有任意数量的类型,而且我们不需要使用名称successfailure)。设置后,它将显示适当的警报。

在 our 中,.alert(item:content:)我们切换不同的ids,以便我们可以确保为正确的选择显示正确的警报。

这种方法比拥有多个布尔值要容易得多,而且更容易扩展。

Sheets 和 ActionSheets 的附录

Sheets并且ActionSheetsAlerts它们的呈现方式非常相似。有四种呈现方式Sheets

这两个需要Bool绑定:

.sheet(isPresented: Binding<Bool>, content: () -> View)
.sheet(isPresented: Binding<Bool>, onDismiss: (() -> Void)?, content: () -> Void)
Run Code Online (Sandbox Code Playgroud)

这两个需要Identifiable绑定:

.sheet(item: Binding<Identifiable?>, content: (Identifiable) -> View)
.sheet(item: Binding<Identifiable?>, onDismiss: (() -> Void)?, content: (Identifiable) -> View)
Run Code Online (Sandbox Code Playgroud)

因为ActionSheets有两种方式,比如Alerts.

使用Bool绑定:

.actionSheet(isPresented: Binding<Bool>, content: () -> ActionSheet)
Run Code Online (Sandbox Code Playgroud)

使用Identifiable绑定:

.actionSheet(item: Binding<Identifiable?>, content: (Identifiable) -> ActionSheet)
Run Code Online (Sandbox Code Playgroud)

我应该使用哪种绑定?

绑定<布尔>

如果您只需要显示一种类型的AlertSheet或者ActionSheet然后使用Bool绑定,则无需编写一些额外的代码行。

绑定<可识别?>

如果您要显示多种不同类型的Alerts、Sheets 或ActionSheets,请选择Identifiable绑定,因为它更易于管理。


更简单的识别

可识别对象的更简单版本是使用枚举而不将其包装在结构中。在这种情况下,我们需要符合 Identifiable 因此我们需要一个计算属性来存储 id 值。我们还需要确保枚举使用 RawRepresentable 以便我们可以获得唯一的 id 值。我建议使用 Int 或 String。在下面的示例中,我使用的是 Int。

enum Choice: Int, Identifiable {
    var id: Int {
        rawValue
    }

    case success, failure
}
Run Code Online (Sandbox Code Playgroud)

然后在视图中我们可以执行以下操作:

struct ContentView:View {

    enum Choice: Int, Identifiable {
        var id: Int {
            rawValue
        } 
        case success, failure
    }

    @State private var showAlert: Choice? // init this as nil

    var body: some View {
        VStack(spacing: 20) {
            Button(action: {
                self.showAlert = .success

            }, label: {
                Text("Show success alert")
            })

            Button(action: {
                self.showAlert = .failure

            }, label: {
                Text("Show failure alert")
            })
        }
        .alert(item: $showAlert) { alert -> Alert in

            switch alert {
            case .success:
                return Alert(title: Text("Success"), message: Text("You have successfully registered"), dismissButton: .default(Text("OK")))

            case .failure:
               return Alert(title: Text("Failure"), message: Text("You have failed to register"), dismissButton: .default(Text("OK")))
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)