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

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.

Related

how to add a password to a button event which cause the event to proceed or fail

I have a Xcode project which is a questionnaire, that has a few questions in and buttons that pass the data selected from one view to the other using segue tags.
At the end of the questionnaire I have a show data button in which will take the admin to the page with the data on as a whole, my issue is anyone can trigger the show data event and I was wondering how would I go about adding a password so that only the admin can see the data.
I've looked online and haven't seen anything that relates to what I need to happen, most things online go over creating a sign up page.
#IBAction func DataReveal(_ sender: UIButton)
{
ID = ID2.text
date = date2.text
Answer1 = answ1.text
Answer2 = answ2.text
}
self.performSegue(withIdentifier: "Link6", sender: self)
}
This takes the results from the other pages and passes them to the page that has the show data button, I just need to add password protection to it so only the password holder can see the data.
You need to Show UIAlert Action with textfield for pick password from user and match text with your password from Coredata,keychain or with API whatever you want.
UIAlert with textfield code is
let alertController = UIAlertController(title: "Test Title", message: "", preferredStyle: .alert)
alertController.addTextField { textField in
textField.placeholder = "Enter Password"
textField.isSecureTextEntry = true
}
let confirmActionBtn = UIAlertAction(title: "OK", style: .default) { [weak alertController] _ in
guard let alertController = alertController, let textField = alertController.textFields?.first else { return }
print("Password is \(String(describing: textField.text))")
// Just compare Entered String in textfield with your original password password
//if password is matched push or segue your required controller
}
alertController.addAction(confirmActionBtn)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)

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.

How to show one UIAlertController after the other in Swift

I want to show user a message once.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
let defaultsDatafirstTrue = NSUserDefaults.standardUserDefaults()
if let _ = defaultsDatafirstTrue.stringForKey("firstTrue") {
} else {
let alertController = UIAlertController(title: "You can add road markers just do long press on the Map", message: "", preferredStyle: .Alert)
let OKAction = UIAlertAction(title: "OK", style: .Default) { (action:UIAlertAction) in
}
alertController.addAction(OKAction)
self.presentViewController(alertController, animated: true, completion:nil)
defaultsDatafirstTrue.setObject("true", forKey: "firstTrue")
}
}
But I have error Warning: Attempt to present <UIAlertController: 0x7c2a8000> on <****: 0x7d193800> whose view is not in the window hierarchy! Because at the first run the iOS app displays a warning to the user that will use the location determination.
How can I see my message after the system message?
Run the code from viewDidAppear: not from viewWillAppear:.
The problem with viewWillAppear: is that it is called before the view is actually visible, so you cannot present another view from it yet. Also, viewWillAppear: can be actually called multiple times and then cancelled (e.g. for interactive transitions).

UIAlertController button click navigates to a NavigationController's first ViewController

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)
}

Attempt to present ViewController which is already presenting (null)

I am creating a user and logging in the user after using the Facebook SDK. All is as it should be with correct inserting the user into the database. I then want to present an alert and upon dismissal of the alert, I want to load a different storyboard (essentially the main menu).
Now my understanding is the error is an issue with threading. BUT.. I am passing the loading of the storyboard etc in as a block to the alert. So shouldn't it then get called only after the user dismisses the alert? I also tried making it run on the main thread (which was mentioned here) but I am still getting the error.
What am I missing here?
The originating call:
if DBUser.insertDictionary(user.getModelAsStringDictionary()) {
if DBUser.loginUserWithFacebookId(facebookId!){
self.accountCreatedAlert()
}else{
//log error, user could not be logged in.
}
}else{
// log error user account could not be inserted into the database
}
}
The async bundling of the alert text and the switchToMainStoryboard() method:
private func accountCreatedAlert(fn:()){
let asyncMoveToMain = {dispatch_async(dispatch_get_main_queue()) {
self.switchToMainStoryboard()
}}
self.showAlert("You will now be logged in", title: "Account created", fn: asyncMoveToMain)
}
The Alert:
func showAlert(text : NSString, title : NSString, fn:()->Void){
var alert = UIAlertController(title: title, message: text, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
UIApplication.sharedApplication().delegate?.window!?.rootViewController?.presentViewController(alert, animated: true, completion: fn)
}
And where the storyboard gets loaded:
func switchToMainStoryboard() ->Void{
let main = UIStoryboard(name: "Main", bundle: nil)
var viewController = main.instantiateInitialViewController() as UIViewController
viewController.modalTransitionStyle = UIModalTransitionStyle.CoverVertical
self.presentViewController(viewController, animated: true, completion: nil)
}
So I tried a lot of messing about with the dispatch_async and dispatch_sync methods and each time, no matter what I did I was getting a lock condition that froze the UI.
Upon further inspection of the documentation (and my code), I provided the block instead to the AlertControllerAction instead of the presentViewController. Thanks to this post for providing the syntax. The resulting changes are as follows:
// MARK: Alert Related Methods
func showAlert(text : NSString, title : NSString, fn:()->Void){
var alert = UIAlertController(title: title, message: text, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: {(alert: UIAlertAction!) in fn()}))
UIApplication.sharedApplication().delegate?.window!?.rootViewController?.presentViewController(alert, animated: true, completion: nil)
}
And then went about calling it like so:
private func showAccountCreatedAlertThenLoadMainStoryboard(){
self.showAlert("You will now be logged in", title: "Account created", fn: {self.switchToMainStoryboard()})
}
The required sequence is now happening without issue. Hope this helps someone else.
Alternative way is that you can ask GCD to delay with your next presentation, like so:
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
// next present
}