Present View Controller doesn't work in Swift - ios

I have got the below piece of code :
let title = "You are not connected to DropBox"
let message = "Please go back to the main page and try to log in again"
let okText = "OK"
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let okayButton = UIAlertAction(title: okText, style: UIAlertActionStyle.Cancel, handler: nil)
alert.addAction(okayButton)
if dbRestClient == nil{
print("please refresh me")
presentViewController(alert, animated: true, completion: nil)
}`
However my alert doesn't show up. I am calling this alert from a View Controller that actually shows on the page so its not the case where my VC is working behind the scene. Please let me know what I am doing wrong here.

Use self.presentViewController(alert, animated: true, completion: nil) instead.
Should work after adding self.

Related

Alamofire upload Completion block works right on iOS 9 & IOS 11 but wrong on iOS 10

I want Alamofire to upload data to the server and depends on the result:
hide progress alert, dismiss current view controller, show
congratulations alert
hide progress alert, show error alert
My code works fine on iOS 9 and 11, but on iOS 10 on success case only hides progress alert. Users are confused and submit the form again and again
Alamofire.upload(multipartFormData: { MultipartFormData in
MultipartFormData.append((self.model.name?.data(using: String.Encoding.utf8)!)!, withName: "Form[name]")
MultipartFormData.append((self.model.category?.description.data(using: String.Encoding.utf8)!)!, withName: "Form[category]")
if (self.filePreview.count>0) {
for (index,preview) in self.filePreview.enumerated() {
if preview != nil
let data = UIImageJPEGRepresentation((preview?.getImage())!, 80)
MultipartFormData.append(data!, withName: "Form[files]["+String(describing:index)+"]", fileName: "attachment"+String(describing: index)+".JPG", mimeType: preview!.getMime())
}
}
}
debugPrint(MultipartFormData)
}, to: url, encodingCompletion: { (result) in
switch result {
case .success( _, _, _):
progressAlert.dismiss(animated: true, completion: nil)
self.dismiss(animated: true, completion: nil)
let doneAlert = UIAlertController(title: "Success", message: "Your message was sent", preferredStyle: .alert)
let donedOk = UIAlertAction(title: "OK", style: .default, handler: nil)
doneAlert.addAction(donedOk)
self.present(doneAlert, animated: true, completion: nil)
break
case .failure( _):
progressAlert.dismiss(animated: true, completion: nil)
let doneAlert = UIAlertController(title: "Failed", message: "Your message was not sent", preferredStyle: .alert)
let donedOk = UIAlertAction(title: "OK", style: .default, handler: nil)
doneAlert.addAction(donedOk)
self.present(doneAlert, animated: true, completion: nil)
break
}
})
Working with completition handlers and using UIApplication.shared.keyWindow?.rootViewController to find current top ViewController solved problem. Progress alert hides, current view controller dismisses, and success alert appears. Code:
progressAlert.dismiss(animated: true) {
let doneAlert = UIAlertController(title: "Отправлено", message: "Your message was sent", preferredStyle: .alert)
let donedOk = UIAlertAction(title: "Success", style: .default, handler: nil)
doneAlert.addAction(donedOk)
self.dismiss(animated: true) {
let presentingVC = UIApplication.shared.keyWindow?.rootViewController
presentingVC?.present(doneAlert, animated: true, completion: nil)
}
}
Issue with presenting a UIAlertController on the Controller whose view is not in the window hierarchy, firstly you are dismissing the controller & presenting UIAlertController on that dismissed controller.
You have to take a reference of controller on which the current controller is presenting, then after dismissing of current controller present UIAlertController on that previous one.
Update user interface on Main dispatch queue.
DispatchQueue.main.async {
let controller = self.presentingViewController
self.dismiss(animated: true) {
let doneAlert = UIAlertController(title: "Success", message: "Your message was sent", preferredStyle: .alert)
let donedOk = UIAlertAction(title: "OK", style: .default, handler: nil)
doneAlert.addAction(donedOk)
controller?.present(doneAlert, animated: true, completion: nil)
}
}
The order should be.
dismiss progress.
Show alert saying that everything is fine.
(this will obscure the interface so theres no possibility touch
anything behind ) When on step 2 tap over ok.
dismiss the upload controller.
Just to update code by rocky :
There´s no need to switch to MainQueue, Math Thompson on the resulting closure output to DispatchQueue.main.async.
So From my point of view interactions should be like :
switch result {
case .success( _, _, _):
//1
progressAlert.dismiss(animated: true, completion: nil)
//2
let doneAlertController = UIAlertController(title: "Success", message: "Your message was sent", preferredStyle: .alert)
let donedOk = UIAlertAction(title: "OK", style: .default) { (action) in
//3
self.dismiss(animated: true, completion: nil)
}
doneAlertController.addAction(donedOk)
self.present(doneAlertController, animated: true, completion: nil)
break
.
.
.
.
.
.

UIAlertViewcontroller is not being popped before going to viewcontroller

I want after signup the alert controller should pop up and then go the loginFirstViewController but this is not going to happen why?? it only goes to loginfirstviewcontroller instead of poping up alert controller
if error == nil {
FIRAuth.auth()?.currentUser!.sendEmailVerification(completion: { (error) in
})
print("You have successfully signed up")
//Goes to the Setup page which lets the user take a photo for their profile picture and also chose a username
let alertController = UIAlertController(title: "Successful!", message: "Email Verification link sent", preferredStyle: .alert)
let alertActionOkay = UIAlertAction(title: "Okay", style: .default)
let vc = self.storyboard?.instantiateViewController(withIdentifier: "LoginFirstViewController")
self.present(vc!, animated: true, completion: nil)
alertController.addAction(alertActionOkay)
self.present(alertController, animated: true, completion: nil)
}
You directly opened new viewcontroller to prevent this you should add completion handler for uialertaction. When the user press ok button you can open other viewcontroller
let alertController = UIAlertController(title: "Successful!", message: "Email Verification link sent", preferredStyle: .alert)
let alertActionOkay = UIAlertAction(title: "Okay", style: .default) { (action) in
let vc = self.storyboard?.instantiateViewController(withIdentifier: "LoginFirstViewController")
self.present(vc!, animated: true, completion: nil)
}
alertController.addAction(alertActionOkay)
self.present(alertController, animated: true, completion: nil)

Firebase login system

So here is the deal. Right now I have a viewcontroller where the user can signup, and it is working (checked in Firebase console). The next step is that I have another view with two fields and a Log in button. That login button now has a segue to the third view, home. But it is possible to click it even though nothing has been entered in the two textfields. The button should only work if the user enters his or he details he made in the signup view, thus logging in.
How can I do this?
already have the login code:
#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)
}
In the second if statement you see that it clears the fields to show the login worked.
What i need is that users can only login if the correct details are filled in, that are in the Firebase database.
Instead of checking if there isnt any errors, check that there is data is the user object like this.
FIRAuth.auth()?.signInWithEmail(self.emailField.text!, password: self.passwordField.text!) { (user, error) in
if user != nil
{
let VC = storyboard?.instantiateViewControllerWithIdentifier("Identifier") as! AccountViewController //The file that controls the view
self.presentViewController(VC, animated: true, completion: nil)
}
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)
}
I managed to fix it by adding self. infront of
storyboard?.instantiate.
Thus creating:
self.storyboard?instantiate.

Trying to present 2 UIAlertControllers back to back

I have a UIAlertController in which the options are populated from an array and are presented to the user. The user then selects an option from the alert. After this, I have a separate alert that provides the user with a confirmation message that has an okay button.
myAlert.addAction(UIAlertAction.init(title: item, style: .Default, handler: {
(UIAlertAction) in
self.chosenBusiness.append(businessNameData[item]!)
}))
self.presentViewController(myAlert, animated: true, completion: nil)
The code above gathers the data from the array and pushes it into actions in myAlert. The code above is inside of a for loop.
After this I use a function to retrieve the topmost view controller, and then push the next alert.
let top = topMostController()
let alertController = UIAlertController(title: "Location pinned", message: "You've successfully pinned this location, good work!", preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) {
(result : UIAlertAction) -> Void in
print("OK")
}
alertController.addAction(okAction)
self.presentViewController(myAlert, animated: true, completion: nil)
top.presentViewController(alertController, animated: true, completion: {
_ in
})
The error I receive is:
Attempting to load the view of a view controller while it is
deallocating and is not allowed and may result in undefined behavior.
UIAlertController: 0x1535b1cd0.
Can someone help me with this?
I think this is what you are looking for. The second must be called with the dismissal action of the first. Also, anytime you work with UI, It is safer to use dispatch_async(dispatch_get_main_queue()) {
\\code }
than not if you are not positive you are currently on the main queue.
let firstAlertController = UIAlertController(title: "First", message: "This is the first message.", preferredStyle: UIAlertControllerStyle.Alert)
let secondAlertController = UIAlertController(title: "Second", message: "This is the second message.", preferredStyle: UIAlertControllerStyle.Alert)
let secondDismissAction = UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, completion: nil)
secondAlertController.addAction(secondDismissAction)
let firstDismissAction = UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default) {
UIAlertAction in
dispatch_async(dispatch_get_main_queue()) {
self.presentViewController(secondAlertController, animated: true, handler: nil)
}
}
firstAlertController.addAction(firstDismissAction)
self.presentViewController(firstAlertController, animated: true, completion: nil)

swift: view is not in the view hierarchy

I'm having a homeView which is the initial view to appear when app launches. In its viewWillAppear() method I check if the user is logged in. If not, he's redirected to login page and if he's not yet registered he'll be directed to sign-up page. I get an error when user clicks the sign-up button. The signUPView appears but it gives a warning that Attempt to present loginViewController on signupViewController whose view is not in the window hierarchy. I have show detail segues between sign-up and login views.
I'm performing the segue after uialert is presented.
I got solutions to put the code in viewDidAppear or ViewWillAppear method but I'm presenting the viewControllers upon button press. So I can't use these methods.
I have searched for a lot of solutions but unable to get the right one.
Please help!
Thank you.
segue triggering code from sign-up :
dispatch_async(dispatch_get_main_queue()) {
if self.registerSuccess == true {
let alert = UIAlertController(title: "Registered!", message: "You have registered successfully!", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: { (action) -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
self.nameTextField.text = ""
self.emailTextField.text = ""
self.passwordTextField.text = ""
self.performSegueWithIdentifier("loginSegue", sender: self)
}))
self.presentViewController(alert, animated: true, completion: nil)
} else {
let alert = UIAlertController(title: "Registration Failed!", message: "Please try again", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: { (action) -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
}))
self.presentViewController(alert, animated: true, completion: nil)
}
}
screenshot of my storyboard

Resources