I'm having a button on view that contains this condition:
FIRAuth.auth()?.addStateDidChangeListener { auth, user in
if let user = user {
self.performSegue(withIdentifier: "AddDevice", sender: nil)
}
else {
let alert = UIAlertController(title: "Sorry", message:"You Have to register", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default) { _ in })
self.present(alert, animated: true){}
}
}
To check whether the user is logged in or not!
If logged in the segue should run and open the next view
If not an alert should appear to the user.
But the next view contains a function that retrieved the current user data! (Logged in user) in viewDidLoad function!
So when the user not logged in and I click to this button I keep getting crash instead of the alert!
How can I prevent viewing the next view and just present the alert!
So I can avoid the crash?
From your description is sounds like the segue is always executing, but it's not what you want.
Connect the segue from the view controller itself, name it and put the whole method inside IBAction connected to the button.
Something like this:
#IBAction func loginDidTouch(sender: AnyObject) {
FIRAuth.auth()?.addStateDidChangeListener { auth, user in
if let user = user {
self.performSegue(withIdentifier: "AddDevice", sender: nil)
}
else {
let alert = UIAlertController(title: "Sorry", message:"You Have to register", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default) { _ in })
self.present(alert, animated: true){}
}
}
For illustration:
In the first image the segue is connected to the button. Therefore, every time the button is pressed it will execute the segue.
In the second image the segue is connected to the view controller and the button is only connected to the via the IBAction. Therefore, the button only triggers the action and the action triggers the segue.
This could easily checked by selecting the segue in storyboard
Related
I have a storyboard controlled app. On the register page, I want to send the user to the home page when pressing the register button. So i dragged a segue from the button to the home page. But then I cannot check conditions before the segue is performed. But if i create a segue and perform it programmatically, the home page comes over the register page, allowing the user to swipe back. Can someone tell me how to check conditions before that segue is performed, or not allow the user to go back to the register page if doing it programmatically. This is my storyboard. Main.storyboard
Xcode 12.0 Swift 5.0
First of all you need to connect "Register" button to an IBAction in your code;
In the IBAction you can call function:
func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil).
If in condition everything is succesfull you call this function otherwise return with error.
Example of sign out button:
private func showLoginViewController() {
// Creates the view controller with the specified identifier
let vc = storyboard?.instantiateViewController(withIdentifier: "loginForm") as! LoginViewController
let navigationVC = UINavigationController(rootViewController: vc)
navigationVC.modalPresentationStyle = .fullScreen
present(navigationVC, animated: true, completion: nil)
}
// Tap on button
#IBAction func signOutUserButton(_: UIButton) {
let alertController = UIAlertController(title: nil, message: "Are you sure you want to sign out?", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Sign Out", style: .destructive, handler: { _ in
// Condition where I check possibility to sign out
self.signOut()
}))
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alertController, animated: true)
}
private func signOut() {
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
// If everything is okay then perform segue
showLoginViewController()
} catch let signOutError as NSError {
// Otherwise show error
print("Error signing out: %#", signOutError)
}
}
Reconnect the segue from the controller to the destination.
Connect the button to an IBAction.
In the IBAction check conditions and call performSegue(withIdentifier:sender:) on success.
I want to ask the user for permission (for which I am using UIAlert) before performing the segue. Once they have answered the question in the alert, I want to segue to the next View Controller, irrespective of their answer.
The code looks something like this:
showAlert() //Method showing the alert
performSegue(withIdentifier : "secondVC", sender : self)
The problem I am facing here is that the app is showing me the alert but not performing the segue.
Add a completion handler to your alert's dismiss button like this
let alert = UIAlertController(title: "Alert", message: "Content Here", preferredStyle: .alert)
// add textfield or whatever you need
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
self.performSegue(withIdentifier: "secondVC", sender: self)
}))
present(alert, animated: true)
The completion handler will be called when the user presses the "OK" button on the alert.
I'm trying to link a specific View Controller that will run when the user picks 'yes' off of a different pop up automatically generated after a random number is chosen.
How would I add the second view controller only when the user picks yes (and discards the pop up when the user picks no)?
I researched it on Stack already and someone suggested using Storyboard and Segues, so I have connected the first and second view controllers using "Show Segue to Weapon Pop Up View Controller", and the second VC has the storyboard ID of "PopUp".
Thanks for any help!
Here's my code:
#IBAction func rolld20(_ sender: Any) {
let randomnumber20 = Int.random(in: 1...20)
d20label.text = String(randomnumber20)
//create the alert
let d20alert = UIAlertController(title: "You rolled a D20!",
message: "You rolled a \(String(randomnumber20))! Is this enough?", preferredStyle: UIAlertController.Style.alert)
// add the actions (yes/no buttons)
d20alert.addAction(UIAlertAction(title: "Yes", style: UIAlertAction.Style.default, handler: nil)) {
//code to open up second view controller with the storyboard ID "PopUp" goes here I believe?
}
d20alert.addAction(UIAlertAction(title: "No", style: UIAlertAction.Style.cancel, handler: nil))
// show the alert when the user clicks to roll d20
self.present(d20alert, animated: true, completion: nil)
}
You need to implement performSegueWithIdentifier.
In your case, the code would be something like:
#IBAction func rolld20(_ sender: Any) {
let randomnumber20 = Int.random(in: 1...20)
let d20alert = UIAlertController(title: "You rolled a D20!",
message: "You rolled a \(String(randomnumber20))! Is this enough?", preferredStyle: UIAlertController.Style.alert)
d20alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: { (action) -> Void in
self.performSegue(withIdentifier: "PopUp", sender: nil)
}))
d20alert.addAction(UIAlertAction(title: "No", style: UIAlertAction.Style.cancel, handler: nil))
self.present(d20alert, animated: true, completion: nil)
}
I removed the comments for readability. Have in mind that the string identifier in the method is relative to the SEGUE identifier, not the destination view controller identifier. To set your segue identifier, simply click on the segue:
And edit the identifier on "Show the Attributes inspector" item:
I'm implementing Login in iOS. I want to unwind to the previous VC(IntroVC) first, and to show completed sign up alert about completed in IntroVC.
(completingSignUp is Unwind function.)
#IBAction func completingSignUp(_ segue: UIStoryboardSegue) {
let completedAlert = UIAlertController(title: "Completing", message: "Congratulations.", preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .default, handler: nil)
completedAlert.addAction(ok)
self.present(completedAlert, animated: true)
}
It shows only the alert without unwind to IntroVC.
I think the location of the alert code seems to be incorrect.
Where should I write alert code?
Put the code into IntroVC‘s viewDidAppear. You will need to introduce a condition so that the alert is presented only when you are coming back via the unwind segue.
Edit: Thank you for the replies, yes I needed an alert and not an action sheet! I have implemented this new code and it works, but is there a reason why it segues to the next view before the user can enter a ride title?
Also it throws this message in the debug console, should I be concerned?
2016-02-16 12:30:21.675 CartoBike[687:128666] Presenting view controllers on detached view controllers is discouraged .
#IBAction func stopAction(sender: UIButton) {
let alert = UIAlertController(title: "Ride Stopped", message: "Give a title to your ride", preferredStyle: .Alert)
let saveAction = UIAlertAction(title: "Save", style: .Default,
handler: { (action:UIAlertAction) -> Void in
// Allow for text to be added and appended into the RideTableViewController
let textField = alert.textFields!.first
rideContent.append(textField!.text!)
})
let cancelAction = UIAlertAction(title: "Cancel",
style: .Default) { (action: UIAlertAction) -> Void in
}
alert.addTextFieldWithConfigurationHandler {
(textField: UITextField) -> Void in
}
alert.addAction(saveAction)
alert.addAction(cancelAction)
// Save the ride
saveRide()
// Automatically segue to the Ride details page
self.performSegueWithIdentifier("ShowRideDetail", sender: nil)
presentViewController(alert, animated: true, completion: nil)
timer.invalidate()
self.stopLocation()
}
Overview:
I am working my way through my first real app. The basic logic is a home screen to start a new ride or view previous rides. Starting a ride will open a new view with a map to record a bicycle ride based on the users location, this can be stopped and save the ride and immediately switch to a new view to see a map of the ride. Alternatively from the home screen the user can select previous rides and view a list of their old rides in a table view and select one and transition to a detailed view with a map of their ride.
Problem: When adding a UIAlertAction I would like there to be a save and cancel feature. In addition, I would like the user to be able to add a custom title by typing it in via a text field. The input from the text field will be appended to a global variable called rideContent that is tied to the creation of new cells in the table view to store multiple bike rides by unique title.
Research:
I have reviewed the questions titled "How to add a TextField to UIAlertView in Swift" & "Writing handler for UIAlertAction" and still can't seem to discern what I have done wrong. Ideally the alert within this screenshot of an app from the raywenderlich site is what I would like it to look like. I am not sure if what I am trying to do is even possible since there a so many view controllers involved. Granted I am new to swift and I'm sure I am missing something obvious!
Currently getting an error of
"Type of expression is ambiguous without more context", see screenshot: here
Here is the UIAlertController code:
// The timer pauses & the location stops updating when the the stop button is pressed
#IBAction func stopAction(sender: UIButton) {
var inputTextField: UITextField?
let actionSheetController = UIAlertController (title: "Ride Stopped", message: "Add a title to your ride", preferredStyle: UIAlertControllerStyle.ActionSheet)
// Add a cancel action
actionSheetController.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil))
// Add a save action
actionSheetController.addAction(UIAlertAction(title: "Save", style: UIAlertActionStyle.Default, handler: {
(actionSheetController) -> Void in
//Add a text field --- Getting an error on this line
actionSheetController.addTextFieldWithConfigurationHandler { textField -> Void in
// you can use this text field
inputTextField = textField
// Append the ride title to the table view
rideContent.append(textField!.text!)
// Update when user saves a new ride to store the ride in the table view for permanent storage
NSUserDefaults.standardUserDefaults().setObject(rideContent, forKey: "rideContent")
// Save the ride
self.saveRide()
// Automatically segue to the Ride details page
self.performSegueWithIdentifier("ShowRideDetail", sender: nil)
}}))
//present actionSheetController
presentViewController(actionSheetController, animated: true, completion: nil)
timer.invalidate()
self.stopLocation()
}
Thank you stack overflow for any help you may offer!
You're using an .ActionSheet while the tutorial you showed is using an .Alert
Action Sheets can have buttons but not text fields.
"Alerts can have both buttons and text fields, while action sheets only support buttons."
NSHipster
Use your tableview Array when Add Button is pressed and "Add alertview textfirld to tableview array and then reload tableview".
#IBAction func btnAdd(_ sender: Any) {
let alertController = UIAlertController(title: "Add Category", message: "", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Add", style: .default, handler: { alert -> Void in
let firstTextField = alertController.textFields![0] as UITextField
self.categories.add(firstTextField.text!)
self.tblCatetgory.reloadData()
})
let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: {
(action : UIAlertAction!) -> Void in })
alertController.addTextField { (textField : UITextField!) -> Void in
textField.placeholder = "Enter Category!!!"
}
alertController.addAction(saveAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
}