I'm following a course online, the app I'm building is basically a cut down version of Instagram as you can see on the left hand side I have a view for signing in, on the right hand side I have a view for signing up.
Pretty straight forward right, now I have created a Seque from the signin page from the button Register which when pressed takes you to the registration view (one on the right), this works as expected however if I'm on the signin page and click sign in and it errors for whatever reason I display the error message but straight after is performs the Seque to the registration screen even though I never pressed register
This is my storyboard:
This is my code behind the Sign In button located on the left view
#IBAction func btnSignIn(sender: AnyObject) {
if txtUsername.text == "" || txtPassword.text == "" {
displayAlert("Error", message: "Username and Password required!")
}
}
I have no other code inside this controller which would cause the Seque to initialise.
If someone can shed some light into how I can stop this Seque from happening on Sign In click and only happen when I press Register I'd appreciate it.
Update
After further investigation this seems to be an issue with my alert box I'm displaying. If I comment out the alert box and press signin and let it error yet not display anything then click register which takes me to the view on the right, click sign in and get taken back to view on the left and then press Sign In display the alert box and when it closes it seems like it thinks the view controller that's being displayed in the one previous to the current one on the page, which is odd. This is my alert function:
func displayAlert(title: String, message: String) { // Display alert message to user. Passing in title and message which will be displayed.
if #available(iOS 8.0, *) {
let a = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
a.addAction((UIAlertAction(title: "OK", style: .Default, handler: { (action) -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
})))
self.presentViewController(a, animated: true, completion: nil)
} else {
// Fallback on earlier versions
}
}
You don't need to dismiss the alert view controller in the action. When the user selects an action, the alert controller is automatically dismissed.
Since you are calling dismissViewControllerAnimated you are going back to the previous view controller.
Right click on Sign-in button and check Connections Inspector, may be you have copy-pasted these two buttons so the action of both sign-in and register button would be same.
Related
I am new iOS programming and now am fascinated in using MaterialComponents which provide by google. Now i facing one problem in component named Dialog.
When the view has been pop up on screen when i touch outside that pop up view and then that view has been dismiss. I don't want that to happen in my app.
I don't want user to click outside popup view to dismiss that popup view. What i want i just want user to click on action button that i provide for user's choice then the view should be dismiss when click on that action button only.
Really glade that you help.
MDCAlertController is inherited from UIViewController.
So, in order to restrict user to click outside MDCAlertController you have to access its property named view and then superview?.subviews[0].isUserInteractionEnabled = false
I have completed one example using MDCAlertController
let alert = MDCAlertController(title: title, message: message)
alert.buttonTitleColor = UIColor(red:0.03, green:0.62, blue:0.09, alpha:1.0)
//MDCAlertControllerThemer.applyScheme(alertScheme, to: alert)
let okayAction = MDCAlertAction(title: "Okay") { (action) in
print("User click okay")
}
let cancelAction = MDCAlertAction(title: "Cancel", handler: nil)
alert.addAction(okayAction)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: {
// When the Dialog view has pop up on screen then just put this line of code when Dialog view has completed pop up.
alert.view.superview?.subviews[0].isUserInteractionEnabled = false
})
use this.
let alert = MDCAlertController(title: title, message: message)
alert.mdc_dialogPresentationController.dismissOnBackgroundTap = false
https://material.io/develop/ios/components/dialogs/api-docs/Categories/UIViewController_28MaterialDialogs_29.html
https://material.io/develop/ios/components/dialogs/api-docs/Classes/MDCDialogPresentationController.html#/c:objc(cs)MDCDialogPresentationController(py)dismissOnBackgroundTap
I am getting a very strange error. I think the compiler is trying to tell me that it can't segue to another view controller until it is done executing all the code in my current view controller but I am not sure.
I'm literally getting input by using an alert box (i.e. calling a function called generateTextField).
Then when I am done I'm saying "Hey I want you to go to another view controller" - but the the compiler instead tells me "Hey I don't think so".
Here is my error:
Warning: Attempt to present HairStyle1ViewController: 0x7...> on browseBarbersViewController: 0x7...> which is already presenting
Warning: Attempt to present HairStyle1ViewController: 0x7..> on browseBarbersViewController: 0x7...> which is already presenting
#IBAction func AddNewStyleButtonClicked(sender: AnyObject)
{
// Get the "hairstyle name" from the user
generateTextField();
// OK We are done with that function, now transition to the
// next screen
performSegueWithIdentifier("HairStyle1", sender: self);
}
// Generate a text field for user input (i.e. call the alert function)
func generateTextField()
{
//1. Create the alert controller.
var tempStyle = "";
var alert = UIAlertController(title: "Add a New Style", message: "Enter the name of the new hairstyle below", preferredStyle: .Alert);
//2. Add the text field. You can configure it however you need.
alert.addTextFieldWithConfigurationHandler({ (textField) -> Void in
textField.placeholder = "Your New Hairstyle Goes Here..";
})
//3. Grab the value from the text field, and print it when the user clicks OK.
alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: { (action) -> Void in
let textField = alert.textFields![0] as UITextField
tempStyle = textField.text!;
print("New Style Added is: " + tempStyle);
HairStyle = tempStyle;
}))
// 4. Present the alert.
self.presentViewController(alert, animated: true, completion: nil)
}
It's also weird that when I take out the generateTextField() function it performs the segue perfectly. I'm very confused.
Wow, I figured it out. I had to instead, segue in the body of the alert function.
I fixed this by adding
self.performSegueWithIdentifier("HairStyle1", sender: self);
after the
HairStyle = tempStyle; line
I'd like to figure out a way so that, if the user presses the "Cancel" button (which I don't believe can be removed) in an ABPeoplePickerNavigationController, the view controller either doesn't close, or will be automatically reopened.
For example, given the following:
var picker = ABPeoplePickerNavigationController()
picker.peoplePickerDelegate = self
self.presentViewController(picker, animated: true, completion: nil)
I'd like to be able to do something like:
if (self.presentedViewController != picker && !userContinuedPastPicker) {
//where userContinuedPastPicker is a boolean set to false
//in a delegate method called when the user clicks on an a contact
//(meaning the user didn't press the cancel button but instead clicked on a contact)
//create and present a UIAlertAction informing the user they must select a contact
//present picker again
self.presentViewController(picker, animated: true, completion: nil)
}
This doesn't work; however, because the if statement won't "wait" until the user has pressed the cancel button or pressed a contact.
I'm not sure there is a way to remove the cancel button, or prevent it from working, but you could respond to the func peoplePickerNavigationControllerDidCancel(_ peoplePicker: ABPeoplePickerNavigationController!) delegate to handle the case where the cancel button is pressed.
I would recommend rather than immediately reopening the picker, you open an alert telling the user they need to pick someone, then open it back up from there. It may feel broken if they cancel and it immediately opens back up.
Reference
edit:
Presenting an alert or the picker probably needs to be delayed long enough for the previous picker to close. dispatch_after
I am creating an app, and to access the main screen of the app the user has to input there fingerprint. I have it setup so that when the fingerprint is correct it programmatically performs a segue to a navigation controller which is connected to the main view controller. Here is my code:
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Authenticate with Touch ID"
context.evaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reason, reply:
{(succes: Bool, error: NSError!) in
if succes {
self.showOrHide(true)
ProgressHUD.showSuccess("Success")
self.performSegueWithIdentifier("passwordCorrectSegue", sender: nil)
} else {
}
})
} else {
self.touchIDLabel.hidden = true
self.touchIDImage.hidden = true
}
The problem is when I perform the segue and it goes to the navigation controller which shows the view controller, the UIBarButtonItem's do not show on the top left and top right of the screen. You can still click on the top left and top right of the screen and the actions for those buttons would run. The problem is that the UIBarButtonItem's are just not showing. Another thing I have tried is that you also have the option to enter in a password, and when the password is correct it goes to the next view controller... and it works perfectly. Does anyone know how to fix this?
The UIBarButtonItems just don't show when I use the method
self.performSegueWithIdentifier("passwordCorrectSegue", sender: nil)
when trying to perform that method using the fingerprint method.
I had the exact same issue: I have 2 VC, each being able to segue to a third one ; if I segue from the first, the right bar button item is not visible (but still works), but if I segue from the second one, the right bar button item is visible.
I guess it's a bug in iOS9.
The workaround I used was to force the initialization of the right bar button item in the destination view controller "viewDidLoad":
override func viewDidLoad() {
super.viewDidLoad()
// Force right bar button item when using performSeguewithIdentifier (bug in iOS9?)
navigationItem.rightBarButtonItem = UIBarButtonItem(
image: UIImage(assetIdentifier: .Profile),
style: UIBarButtonItemStyle.Plain,
target: self,
action: Selector("displayUser"))
}
That fixed the issue for me (at least until Apple fixes this bug).
The code continually segues into the next view controller...even with performSegueWithIdentifier() commented out. It doesn't matter if the text fields are blank (which should prompt up an alert) or if the username/password is entered correctly, it just segues anyway.
I've tried Clean -> Build -> Run, as well as removing the view controller in Storyboard and re-adding a new one. What's the issue here?
PFUser.logInWithUsernameInBackground(userEmail, password: userPassword) {
(user: PFUser?, error: NSError?) -> Void in
//If user is found in Parse, log in
if user != nil {
var successAlert = UIAlertView(title: "Success", message: "Logged In", delegate: self, cancelButtonTitle: "OK")
successAlert.show()
//Transition to FacebookViewController
//self.performSegueWithIdentifier("loginToFacebookSegue", sender: self)
}
//Display warning - incorrect login
var warningAlert = UIAlertView(title: "Try again", message: "Username/password incorrect", delegate: self, cancelButtonTitle: "Dismiss")
warningAlert.show()
}
Your problem is that since your segue is attached to a UIButton, it is automatically called when the button is pressed. Your block is never really getting called. To fix this, drag your connection from view controller to view controller. Make sure you are zoomed out in the storyboard, then just create the connection (option dragging from a black spot in the view controller itself not the button). Make sure to give your segue an identifier.
Then call performSegueWithIdentifier when you need to run the segue.