SwiftUI Button action statements - ios

I'd like to do a statement in button block, if textfield is empty, then get an alert and you can't acces the other ViewController, if you fill he textfield, then you can start play:
LaunchView.swift :
Button(action: {
if username.isEmpty {
showingAlert = true
} else {
//Here I would like to acces another view
}
}) {
Text("PLAY")
.padding()
.foregroundColor(.black)
.frame(width: 350)
.background(Color.white)
.clipShape(Capsule())
.shadow(radius: 15)
}.alert(isPresented:$showingAlert) {
Alert(
title: Text("Error"),
message: Text("Enter your username"),
dismissButton: .cancel()
)
}
And I have one more question, how could I use a variable from LaunchView, in FirstView?
Like, I would like to get the name from LaunchView's textfield, in alerts in FirstView:
"(username), your score is X"
FirstView.swift:
.alert(isPresented: $showingScore) {
Alert(title: Text(scoreTitle), message: Text("Your score is \(point)"), dismissButton: .default(Text("Continue")) {
self.askQuestion()
})
}

Related

Minimize or bring back sheet in SwiftUI

I have a Voip calling app using CallKit and when call received, it will open a view call IncomingView in a sheet in my SwiftUI app. So far so good. But i want to minimize the sheet and can navigate to other pages and preferably shows a green bar at the navigation (similar to WhatApp) that indicates the call is going on and when i tap there, it should bring back my "IncomingView".
here is my code:
struct MainView: View {
let acceptPublishser = NotificationCenter.default
.publisher(for: Notification.Name.DidCallAccepted)
let endPublisher = NotificationCenter.default
.publisher(for: Notification.Name.DidCallEnd)
var body: some View {
NavigationView {
TabView {
TabListView()
.tabItem {
Label("Home", systemImage: "house.fill")
}
ContactListView()
.tabItem {
Label("Contacts", systemImage: "person.crop.circle")
}
ProfileView()
.tabItem {
Label("Profile", systemImage: "person.crop.circle")
}
}
.padding(0)
.onAppear(){
self.showModal = MyCallDelegate.shared.isIncomingCall
}
.sheet(isPresented: $showModal){
IncomingCallView() // -> Present IncomingCallview() as sheet
}
.accentColor(Color(.green))
}.onReceive(self.acceptPublishser, perform: { output in
showModal = true
})
.onReceive(self.endPublisher, perform: { output in
showModal = false
})
}
}
struct IncomingCallView: View {
var body: some View {
VStack{
Spacer()
Text("callerId").foregroundColor(.white)
Text("Timer").foregroundColor(.white)
}
}

SwiftUI List NavigationView onDelete alert confirmation has ugly animation

The problem is that whenever you put the list in navigationView, the animation of the delete cancelation of the list row is not so nice. Am I doing something wrong in my body property?
var body: some View {
NavigationView {
VStack {
List {
ForEach(self.contacts){ contact in
ContactRow(contact: contact)
}.onDelete { self.setDeletIndex(at: $0) }
}
.alert(isPresented: $showConfirm) {
Alert(title: Text("Delete"), message: Text("Sure?"),
primaryButton: .cancel(),
secondaryButton: .destructive(Text("Delete")) {
self.delete()
})
}
.listStyle(PlainListStyle())
.navigationTitle("Contacts")
.navigationBarItems(trailing: HStack {
Button("Add", action: self.addItem)
})
}
}
}

Conditionally navigate to new view in swiftui

I tried to navigate to my user view in login submit function with swiftui.
struct LoginUIView: View {
...
var body: some View {
VStack {
...
Button(action: {
self.login(name: self.name, pass: self.pass)
}) {
Text("Login")
.padding()
.frame(maxWidth: .infinity)
.foregroundColor(Color.white)
.cornerRadius(4.0)
.background(Color("light-green"))
}
}
.padding()
}
func login(name: String, pass: String) {
// auth with name and pass
// then try to conditionally navigate to specific view
if ... {
// navigate to user view
}
if ... {
// navigate to other view
}
}
}

Multiple Alerts in one view can not be called SwiftUI

The method of calling an alert in swiftUI is supposed to be simpler than ever, a simple example of this would be
struct ContentView: View {
#State private var showingAlert = false
var body: some View {
Button(action: {
self.showingAlert = true
}) {
Text("Show Alert")
}
.alert(isPresented: $showingAlert) {
Alert(title: Text("Important message"), message: Text("Wear sunscreen"), dismissButton: .default(Text("Got it!")))
}
}
}
However, even though I am calling an alert and can verify the method is being called, my alert does not display.
In my code I have the following Struct
private struct resetButton: View {
#State var isResetting = false
var body: some View {
Button("Reset"){
self.isResetting = true
}.alert(isPresented: $isResetting) {
print("We get here")
return
Alert(title: Text("Are you sure?"), message: Text("This will erase your Access Key."), primaryButton: .destructive(Text("Reset")) {
clearToken()
clearAccessKey()
clearUsername()
}, secondaryButton: .cancel(Text("Cancel")))
}
}
}
Which is called in the main view simply by resetButton().
However, when I push this button, despite isResetting equalling true, the alert does not display. I have verified that my .alert method is being called as the console prints
we get here
the first time the button is pushed.
However, the Alert is never displayed.
An alert is displayed correctly elsewhere in this same view with
.alert(isPresented: $failedLogin) {
self.password = ""
return
Alert(title: Text("Invalid Login"), message: Text("Please verify your credentials and try again"), dismissButton: .default(Text("Ok")))
}
When a #State variable named failedLogin is set true, however, the aforementioned alert is never presented.
My first impression is that this may be a bug in Swift, if it is I'll report it to apple. However, maybe something I'm doing isn't working because of an error on my part.
Edit:
As clarified in the answer below, the problem seems to be relating to the fact that my resetButton() was trying to throw up an alert in a view that already contains an alert. Apparently you can't have multiple alerts in one view.
You can show alerts dynamically by using .alert(item:) instead .alert(isPresented:):
struct AlertItem: Identifiable {
var id = UUID()
var title: Text
var message: Text?
var dismissButton: Alert.Button?
}
struct ContentView: View {
#State private var alertItem: AlertItem?
var body: some View {
VStack {
Button("First Alert") {
self.alertItem = AlertItem(title: Text("First Alert"), message: Text("Message"))
}
Button("Second Alert") {
self.alertItem = AlertItem(title: Text("Second Alert"), message: nil, dismissButton: .cancel(Text("Some Cancel")))
}
Button("Third Alert") {
self.alertItem = AlertItem(title: Text("Third Alert"))
}
}
.alert(item: $alertItem) { alertItem in
Alert(title: alertItem.title, message: alertItem.message, dismissButton: alertItem.dismissButton)
}
}
}
So i'm still not 100% sure why this was unable to post an alert this way? But I was able to fix this issue simply by placing the alert in the main view.
I changed the #State variable isResetting to a binding bool and paced a new state variable in the main class. I then just copied over the .alert and this seems to solve the issue.
The struct now looks like this
private struct resetButton: View {
#Binding var isResetting: Bool
var body: some View {
Button("Reset"){
self.isResetting = true
}
}
}
and the alert is the same but is now in the class calling resetButton().
Edit:
So it appears that SwiftUI won't let you call multiple alerts from the same view, however, if you want multiple alerts in the same view then there is a way around this.
You can call an alert from anywhere inside of a view so the following solution works.
private struct exampleAlert: View {
#State var alert1 = false
#State var alert2 = false
var body: some View {
Vstack{
Button("alert 1"){
self.alert1 = true
}.alert(isPresented: $alert1) {
Alert(title: Text("Important message"), message: Text("This alert can show"), dismissButton: .default(Text("Got it!")))
}
Button("alert 2"){
self.alert2 = true
}.alert(isPresented: $alert2) {
Alert(title: Text("Important message"), message: Text("This alert can also show"), dismissButton: .default(Text("Got it!")))
}
}
}
}
The catch is here that if either of these alerts were to be placed on the Vstack, one will not function. If they are placed on separate views though then both can be called as expected.
Perhaps this is something that will be rectified in a future update? In the meantime though, here is a solution for working around this problem.
In my case I solved it by adding an empty Text in front of the alert.
private struct exampleAlert: View {
#State var alert1 = false
#State var alert2 = false
var body: some View {
Vstack{
Button("alert 1"){
self.alert1 = true
}
Button("alert 2"){
self.alert2 = true
}
}
Vstack{
Text("")
.alert(isPresented: $alert1) {
Alert(title: Text("Important message"), message: Text("This alert can show"), dismissButton: .default(Text("Got it!")))
}
Text("")
.alert(isPresented: $alert2) {
Alert(title: Text("Important message"), message: Text("This alert can also show"), dismissButton: .default(Text("Got it!")))
}
}
}
}

SwiftUI: How to execute closure when Alert is dismissed?

I've been trying out swiftUI and looked at this Ray Wenderlich tutorial... I noticed they didn't re-implement the "nextRound" functionality... so I tried to do it myself. Ran into a problem (which maybe they did, also):
The basic question is more general:
Using swiftUI, how do you trigger a function when an Alert is dismissed -- when the user clicks "OK." ?
I've tried using the dismissButton argument of the Alert constructor...
(and also the .onDisappear method of View but I can't figure out how to apply it to the Alert view.)
Code:
import SwiftUI
struct ContentView: View {
#State var shouldShowAlert: Bool = false
// this never gets called
func onAlertDismissed() {
print("you will not see this in the console")
}
// this doesn't seem to work
var dismissButton: some View {
Button(action: {
self.onAlertDismissed()
}) {
// Bilbo Baggins does not appear -- "OK" still shows
Text("BILBO BAGGINS")
}
}
var body: some View {
VStack {
Spacer()
Button(action: {
self.shouldShowAlert = true
}) {
Text("show the alert!")
}
Spacer()
}.alert(isPresented: $shouldShowAlert, content: {
// what to add here?
Alert(title: Text("Alert:"), message: Text("press OK to execute onAlertDismissed()..."))
// what I have tried and doesn't work:
/*
Alert(title: Text("Alert:"), message: Text("press OK to execute onAlertDismissed()..."), dismissButton: self.dismissButton as? Alert.Button)
*/
})
}
}
The button is constructed a little differently. You basically have to use a static factory method from Alert.Button to construct them and pass those in.
Alert(title: Text("Alert:"),
message: Text("press OK to execute default action..."),
dismissButton: Alert.Button.default(
Text("Press ok here"), action: { print("Hello world!") }
)
)
Alert(title: Text("Alert!"), message: Text("Message"),
primaryButton: Alert.Button.default(Text("Yes"), action: {
print("Yes")
}),
secondaryButton: Alert.Button.cancel(Text("No"), action: {
print("No")
})
)
It's possible to create alerts like this:
import SwiftUI
struct ContentView: View {
#State var showingAlert = false
var body: some View {
VStack {
HStack {
Button(action: {
self.showingAlert = true
})
{
Text("Save")
.font(.headline)
}
.alert(isPresented: $showingAlert, content: {
return Alert(
title: Text("Save Product"),
message: Text("Are you sure you want to save the changes made?"),
primaryButton: .default(Text("Yes"), action: {
//insert an action here
}),
secondaryButton: .destructive(Text("No")))
})
}
}
}
}
By looking at your code, it appears you don’t include a button in the alert propert, so your alert is not executing any action, in swiftui the alert signature is
init(title: Text, message: Text? = nil, primaryButton: Alert.Button, secondaryButton: Alert.Button)
Implement the signature properly is the first step

Resources