I did some research before posting it here but I was not able to fix it.
In the register View I want the user to register.
I created a linked list and as user registers a username my program checks whether or not the username is already taken.
if it is taken it should give an alert saying that the username is already taken as the user clicks the register button.
if the username is not taken then it should show an alert saying the registration is successful
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)
}
}
}
}
It is possible to do. Though you don't need to track as many states as you are.
Firstly, you only need to track if they have failed or not. So your failedRegister
will track if the user has successfully registered or not. That means we can get remove the successfulRegister
.
We need a variable to track whether an alert is showing or not, for this we will use the variable showAlert
As you have a linked list that provides the userinfo, we will mock that with just an array containing a couple of usernames.
So here is a simplified version of your code that should work.
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")))
}
}
}
}
There is an alternative way to show different Alerts
on the same View
. This is to use a binding to an object that is Identifiable
.
If we look at the ways we can initialise an Alert
on a View
we see there are two ways. The first has the following signature:
.alert(isPresented: Binding<Bool>, content: () -> Alert)
Which is what is used in the example above.
However there is a second way which has the following signature:
.alert(item: Binding<Identifiable?>, content: (Identifiable) -> Alert)
This second way can allow for more complex alerts to be managed. To utilise this we need something to track the state of the alerts. We can create a simple struct that conforms to Identifiable
and contains an enum of the different choices that we have for an alert.
We then create an @State
variable to track the AlertIdentifier
and initialise to nil
so that its state is empty and will not show any alerts until it is changed.
We can then add our .alert(item:content:)
to our View
.
Here is a simple example showing it in action.
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")))
}
}
}
}
Notice that in the buttons we set the showAlert
to be an instance of the struct AlertIdentifier
with the type of alert we want to show. In this case we have two types: success and failure (but we could have as many types as we want, and we don't need to use the names success and failure). When that is set, it will show the appropriate alert.
In our .alert(item:content:)
we switch over the different id
s so that we can make sure that the correct alert is shown for the correct choice.
This method is much easier than having multiple booleans, and it is easier to extend.
Sheets
and ActionSheets
are very similar to Alerts
in how they are presented. There are four ways to present Sheets
.
These two require a Bool
binding:
.sheet(isPresented: Binding<Bool>, content: () -> View)
.sheet(isPresented: Binding<Bool>, onDismiss: (() -> Void)?, content: () -> Void)
These two require an Identifiable
binding:
.sheet(item: Binding<Identifiable?>, content: (Identifiable) -> View)
.sheet(item: Binding<Identifiable?>, onDismiss: (() -> Void)?, content: (Identifiable) -> View)
For ActionSheets
there are two ways, like Alerts
.
With the Bool
binding:
.actionSheet(isPresented: Binding<Bool>, content: () -> ActionSheet)
With the Identifiable
binding:
.actionSheet(item: Binding<Identifiable?>, content: (Identifiable) -> ActionSheet)
If you only need to show one type of Alert
, Sheet
or ActionSheet
then use the Bool
binding, it saves you having to write some extra lines of code.
If you many different types of Alert
s, Sheet
s or ActionSheet
s to show then choose the Identifiable
binding as it makes it much easier to manage.
A simpler version of the identifiable object would be to use an enum without wrapping it in a struct. In this case we need to conform to Identifiable so we need a computed property to store the id value. We also need to make sure that the enum uses a RawRepresentable so that we can get a value for the id that is unique. I would suggest using an Int or a String. In the example below I am using an Int.
enum Choice: Int, Identifiable {
var id: Int {
rawValue
}
case success, failure
}
Then in the view we could do the following:
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")))
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With