UIAlertController button click navigates to a NavigationController's first ViewController - swift

I am new to swift and iOS programming. I have StartViewController which has a button which when clicked a UIAlertController with two buttons - Decline & Accept. When clicking on Accept, I want to navigate to a MyNavigationController's - ViewController. The MyNavigationController has four ViewControllers which I navigate to using a slide menu.
I am attaching a sample code and screenshot of my storyboard for reference.
#IBAction func showAlert() {
let alertController = UIAlertController(title: "Disclaimer", message: "Before using this teaching resource, you confirm that you agree:\n1. To obey the law regarding data protection and patient confidentiality.\n2. To us this app professionally and appropriately in clinical settings.\n3. This is for your personal use and you may not modify, distribute, publish, transfer any information obtained from this teaching resource without the developers' permission.\n4. In no event shall the developer be liable to you for any loss arising from your use of this resource.", preferredStyle: .Alert)
let declineAction = UIAlertAction(title: "Decline", style: .Cancel, handler: nil)
alertController.addAction(declineAction)
let acceptAction = UIAlertAction(title: "Accept", style: .Default) { (_) -> Void in
//I think the code to navigate should go here, help please.
}
alertController.addAction(acceptAction)
presentViewController(alertController, animated: true, completion: nil)
}
Storyboard Screenshot -
In the screenshot above, the Login button opens up a UIAlertController alertController. Accept button on this AlertController should navigate to ViewController on the MyNavigationController.
This MyNavigationController has three other ViewControllers which are navigated using a slide menu.
Thank you in advance.

You need to create a segue between your current view controller and the destination view controller and make sure the segue ID matches the id in your performSegueWithIdentifier call
#IBAction func showAlert() {
let alertController = UIAlertController(title: "Disclaimer", message: "Before using this teaching resource, you confirm that you agree:\n1. To obey the law regarding data protection and patient confidentiality.\n2. To us this app professionally and appropriately in clinical settings.\n3. This is for your personal use and you may not modify, distribute, publish, transfer any information obtained from this teaching resource without the developers' permission.\n4. In no event shall the developer be liable to you for any loss arising from your use of this resource.", preferredStyle: .Alert)
let declineAction = UIAlertAction(title: "Decline", style: .Cancel, handler: nil)
let acceptAction = UIAlertAction(title: "Accept", style: .Default) { (_) -> Void in
self.performSegueWithIdentifier("SomeSegue", sender: self) // Replace SomeSegue with your segue identifier
}
alertController.addAction(declineAction)
alertController.addAction(acceptAction)
presentViewController(alertController, animated: true, completion: nil)
}

Related

Can't move from a VC to another one, can't figure out why

I have a register screen in my app, after the user writes done a phone number and agrees to the terms, and clicking "register" the app should move the user to the next vc. The problem is, although everything looks to be ok, the app won't move to the next screen.
This is my code:
func handleRegister() {
if UserDefaults.standard.object(forKey: USER_AGREED) == nil {
let alert = UIAlertController(title: "Terms and Conditions", message: "Please agree to the terms and conditions to continue", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (alert) in
self.showTermsAndConditions()
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .destructive, handler: nil))
self.present(alert, animated: true)
}else{
guard let userPhoneNumber = self.mView.textField.text else { return }
UserDefaults.standard.set(userPhoneNumber, forKey: USER_PHONE_NUMBER)
let vc = Tabbar()
self.navigationController?.pushViewController(vc, animated: true)
}
}
I tried to put a breakpoint, and it does gets to the else block when it should, and recognizes the Tabbar, but just won't move there.
Any suggestions?
There are two possible sources of trouble here.
You are saying self.navigationController?. The question mark means: "If I have no navigationController, do nothing." If you have no navigationController, you do nothing. Perhaps what you meant to say is self.present(vc, animated:true).
You are saying let vc = Tabbar(). That is not an existing view controller in the interface or storyboard. So when you move there, it might be empty (that depends on how Tabbar view controller is constructed).

How to implement code in two different UIAlertActions from a single UIAlertController?

I am new in Swift and Xcode world. In my app, user has to choose if he/she wants to scan QRCode to get data from it, or if he/she wants to enter data(type String) and generate QRCode via that String. I am trying to implement QRScanner and QRGenerator but I am stuck.
I am using UIAlertController which has two UIAlertActions for now.
After I get my data using QRScanner/QRGenerator, I have to store it in my custom UITableViewCell. Should I be using UIAlertController or something else? Any tips and tricks how should I do this?
https://imgur.com/a/qqhzG8Q
This is what I have for now.
#IBAction func cameraButtonClicked(_ sender: UIBarButtonItem) {
var textField = UITextField()
let alert = UIAlertController(title: "What do you want to do?", message: "", preferredStyle: .alert)
let scanAction = UIAlertAction(title: "SCAN QR", style: .default) { (action) in
}
let manualAction = UIAlertAction(title: "MANUAL", style: .default) { (action) in
}
alert.addAction(scanAction)
alert.addAction(manualAction)
present(alert, animated: true, completion: nil)
}
I tried several different ways to make this work, but my app is constantly crashing. As I said, I am new in this world, so any suggestion is more than welcome.

AlertController trying to be presented on view not in window hierarchy

Trying to figure out why I am getting this error. I have a single view application with two viewcontrollers. I have a segue to the second vc on a button touch:
#IBAction func showAccount(_ sender: Any) {
performSegue(withIdentifier: "toAccountFromRoot", sender: self)
}
I have a facade pattern set up with a Controller file and several helper files. I set the Controller up on the AccountViewController file:
let myController = Controller.sharedInstance
One of the helper files is to set up a central AlertController for simplified alerts (no extra buttons). It has one method, presentAlert that takes a few arguments, alert title, messsage, presenter.
On my AccountViewController I am attempting to present an alert using this code:
myController.alert.presentAlert("No Agent", "You have not registered an agent yet.", self)
and in the AlertManager file, presentAlert looks like this:
func presentAlert(_ title:String, _ message:String, _ presenter:UIViewController) {
let myAlert = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
myAlert.addAction(okAction)
presenter.present(myAlert, animated:true, completion:nil)
}
I've done this in quite a few apps without an issue so I am confused as to why the compiler thinks the AccountViewController view is not in the window hierarchy.
Dang it, typed all that and then I solved it 5 seconds later. Just in case someone else runs into the same problem, I was trying to present the alert in viewDidLoad, should of been doing it in viewDidAppear.

ViewController segue and login

I'm new to Swift (and programming in general). I'm having some difficulties in Xcode with the login feature. What I want is that the user should be logged in if no errors was returned and the user should be sent to another controller. I've read some of the documentation from Apple and the performSegueWithIdentifier (and of course some questions asked here), but it still does not work when I use a segue with push or modal that are given an identifier. Either the app crashes, or the user is sent to my UITabBarController even if the credentials was incorrect.
This is my LogInViewController.swift:
#IBAction func loginAction(sender: AnyObject)
{
if self.emailField.text == "" || self.passwordField.text == ""
{
let alertController = UIAlertController(title: "Oops!", message: "Please enter an email and password.", preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
else
{
FIRAuth.auth()?.signInWithEmail(self.emailField.text!, password: self.passwordField.text!) { (user, error) in
if error == nil
{
self.emailField.text = ""
self.passwordField.text = ""
}
else
{
let alertController = UIAlertController(title: "Oops!", message: error?.localizedDescription, preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
// User Logged in
self.performSegueWithIdentifier("LoggedIn", sender: self)
}
}
}
The error I get in the console:
2016-09-04 14:55:30.019 DrinkApp[37777:1006336] *** Terminating app due to uncaught exception 'NSGenericException', reason: 'Could not find a navigation controller for segue 'LoggedIn'. Push segues can only be used when the source controller is managed by an instance of UINavigationController.'
There are two main ways to send a user to another View Controller. The first is performSegueWithIdentifier. Here's how you would go about using that:
First of all, control-drag from the yellow circle of the original VC to the target VC in the interface builder.
Next, click on the 'segue arrow' that appears between the two View Controllers.
In the identifier field, type in mySegue.
Next, go back to your code. When you reach the sign in section, add the following code:
self.performSegueWithIdentifier("mySegue", sender: nil)
The second method is a bit more advanced. Here's how that would work:
First, click on the yellow circle of the destination VC. Click on the newspaper, then call the storyboard ID myVC.
Make sure to click on Storyboard ID
Go back to the same part of the code and copy and paste this code:
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initViewController: UIViewController = storyboard.instantiateViewControllerWithIdentifier("myVC") as UIViewController
self.presentViewController(initViewController, animated: true, completion: nil)
Comment down below if you have any questions. Good luck!
Here is a Firebase login example:
func login() {
FIRAuth.auth()?.signInWithEmail(textFieldLoginEmail.text!, password: textFieldLoginPassword.text!) { (user, error) in
if error != nil {
let alert = UIAlertController(title: "User Authentication error",
message: error?.localizedDescription,
preferredStyle: .Alert)
let OKAction = UIAlertAction(title: "OK",
style: .Default) { (action: UIAlertAction!) -> Void in
}
let resetPasswordAction = UIAlertAction(title: "OK",
style: .Default) { (action: UIAlertAction!) -> Void in
}
alert.addAction(OKAction)
guard let errorCode = FIRAuthErrorCode(rawValue: (error?.code)!) else { return }
switch errorCode {
case .ErrorCodeWrongPassword:
alert.addAction(resetPasswordAction)
case .ErrorCodeNetworkError:
print("Network error")
default:
print("Other error\n")
break
}
self.presentViewController(alert, animated: true, completion: nil)
} else { // User Logged in
self.performSegueWithIdentifier("MySegue", sender: self)
}
}
}
I only handled two errors for this example.
If you use push segue, you need to have navigation controller, and your root segue should lead to the login view controller, then another segue to the next view controller. This last segue must have an identifier (in this case it's MySegue)
Edit:
Since it seems that your problem is not with Firebase, but with setting up the storyboard. Here is an example:
Set up one UINavigationController two UIViewControllers
Control Drag between the UINavigationController to the middle UIViewControllers and choose root view controller from the popup menu
Control Drag between the UIViewController to the last UIViewControllers and choose show
select the segue added in the previous step and change its identifier (in the attributes inspector) to MySegue.
5.Choose the Middle UIViewControllers and change the class name (in the identity inspector) to your login view controller class.
Here is the full setup:
control drag menu (step 2):
Segue attributes inspector (step 4):
I found this answer mentioned here to be super elegant.
https://fluffy.es/how-to-transition-from-login-screen-to-tab-bar-controller/
it works with tab bars, and nav bars. you create a function in the scene delegate, that switches the root view controller. you access the scene delgates functions and windows with (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?

How to open an input accessory view through a UIAlertAction using swift?

I have a UIAlertController with some UIAlertActions in it as shown below.
let alertController = UIAlertController(title: nil, message : nil, preferredStyle: .ActionSheet)
let addMoreAction = UIAlertAction(title: "Add more", style: .Default) { (action) in
// Want to be able to call an input accessory view from here
}
alertController.addAction(addMoreAction)
self.presentViewController(alertController, animated: true, completion: nil)
When user clicks on the "Add more" action, it should dismiss the UIAlertController and open up the keyboard with a UITextView attached on top of the keyboard. (Like you see in chat apps like Facebook Messenger).
How do I achieve this using swift?
Perhaps you are looking for inputAccessoryView property. You can check this tutorial. Using this you can make your own view that can be shown to the top of the keyboard.