Keyboard dismisses after multiple UIAlertViews are opened - iphone

I am having a weird issue with my keyboard automatically closing and then reopening when I open multiple UIAlertViews. If I have a keyboard (from a separate UITextField) and I display a UIAlertView, then upon dismissal of that alert I open another (opening the second one in the didDismissWithButtonIndex). When I dismiss the 2nd one it dismisses the keyboard which then comes back up. If i try this with more than 2 alerts in a row, it will still close my keyboard after the 2nd alert is dismissed, but it doesn't show up until the last alert is dismissed. The problem is that the keyboard delegate functions are NOT called so I cannot respond to it being dismissed. I have other UI elements (textfield and images) that get shifted when the keyboard opens, so when it closes those elements float in the screen and look strange. Any idea why that keyboard automatically dismisses? Thanks
BTW, I use an NSMutableArray of NSDictionary objects to queue up alerts that need to be displayed if an alert is already displayed. I am not creating and displaying more than 1 alert at a time.
EDIT: Here's sample code. If you run this you'll see both alerts open (0 then 1) after you dismiss '1' you'll see '0' under it. After you dismiss '0' you'll see what I mean - they keyboard briefly closes and opens but no delegate functions are called. If you set i to a value higher than 2, you'll see that the keyboard still closes after dismissing the 2nd alert, but will stay closed until the last alert is dismissed. I also tried opening just 1 UIAlert, and opening the others one at a time from a queue as each one was dismissed, and still noticed that same behavior. Any ideas?
EDIT: After some more digging I found that if I register for the notifications UIKeyboardDidShowNotification and UIKeyboardDidHideNotification they are in fact fired when the keyboard is automatically dismissed and presented. I still would like to know what in the underlying API is causing it to even happen so it can hopefully be avoided.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(0, 100, 320, 48)];
[textField setDelegate:self];
[textField setBackgroundColor:[UIColor redColor]];
[window addSubview:textField];
[textField release];
[self.window makeKeyAndVisible];
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *) textField{
NSLog(#"textFieldShouldReturn called with %#", textField);
[textField resignFirstResponder];
return YES;
}
-(void) textFieldDidBeginEditing:(UITextField *)textField
{
NSLog(#"textFieldDidBeginEditing called with %#", textField);
for (int i=0; i< 2; i++) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: #"test" message: [NSString stringWithFormat:#"%d", i] delegate:self cancelButtonTitle:NSLocalizedString(#"OK",#"") otherButtonTitles:nil];
[alert show];
[alert release];
}
}
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
{
NSLog(#"++++ textFieldShouldEndEditing %#", textField);
return YES;
}
-(void) textFieldDidEndEditing:(UITextField *)textField
{
NSLog(#"++++ textFieldDidEndEditing %#", textField);
}

the keyboard is only shown when the corresponding UI element is the first responder.. somehow multiple alert views modify the responder chain for a short time. Seems like a framework issue..
I would suggest this workaround:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
dispatch_async(dispatch_get_main_queue(), ^{
/* show new alert view here */
});
}
EDIT
actually i now think it has to do with the Window hierarchy of the application. UIAlertViews create their own window (at window level UIWindowLevelAlert), make them the key window to recieve touch input and then make the old window key window again upon dismissal. When you show a new alert view on didDismiss, UIKit seems to lose (temporarily) track of key windows and the responder chain..
The fix above of course still applies.

Related

View locked out after UIActionSheet or UIAlertView (iPhone / iPad)

I'm having some trouble with either a UIAlertView or UIActionSheet stealing the focus and not letting go.
To elaborate, I have a setup whereby a user presses a button. This then displays a UIAlertView with a UITextField. When the user presses the 'Ok' button a UIActionSheet is displayed (called from the UIAlertView delegate method) confirming something with the user. However when they press one of the buttons the UIActionSheet goes away but the focus doesn't get returned to the main view.
I have been playing around and whatever I seem to do I always end up with my main UIView being covered with a layer like this (obviously you can seem my view through the layer). I have tried removing all subviews of the view but it didn't have any success.
Please can someone help me out?
Here is the source code:
// This method displays the UIAlertView which contains a UITextView linked up to a
// UIPickerView.
- (IBAction)buttonPressed:(id)sender
{
UIAlertView *deleteAlert = [[UIAlertView alloc] initWithTitle:#"Delete Something"
message:#"Select something to delete:"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Ok", nil];
deleteAlert.alertViewStyle = UIAlertViewStylePlainTextInput;
deleteAlert.tag = kDeleteAlert;
deleteTextField = [deleteAlert textFieldAtIndex:0];
[pickerView reloadAllComponents];
deleteTextField.inputView = pickerView;
[deleteAlert show];
}
// The UIAlertView delegate method is then used to display a UIActionSheet to confirm
// whether the user wants to proceed.
- (void) alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)buttonIndex
{
UIActionSheet *confirmDelete = [[UIActionSheet alloc] initWithTitle:#"Delete blah?"
delegate:self
cancelButtonTitle:#"No"
destructiveButtonTitle:#"Yes"
otherButtonTitles:nil];
confirmDelete.actionSheetStyle = UIActionSheetStyleBlackOpaque;
[confirmDelete showInView:self.parentViewController.tabBarController.view];
}
// Then the focus should be returned to the original view (i.e. where the first button
// was pressed to launch the UIAlertView. However the focus is still locked and the
// view appears slightly dimmed around the edges.
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == actionSheet.destructiveButtonIndex)
{
// Do some stuff ...
}
}
I think your issue is a result of the deleteAlert alert view not being dismissed when the actionSheet is called.
I sounds to me like the alert view still has focus but is in an unknown state, because it hasn't been dismissed, but you have done something with it's button press.
Presumably, you want to dismiss the alert view when the action sheet is presented? Then when the action sheet is dismissed, you get back to your main view? So, the order you want:
1) Present alert view
2) If a button on the alert view is pressed, it notifies it's delegate using - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
3) Dismiss the alertView - (void)dismissWithClickedButtonIndex:(NSInteger)buttonIndex animated:(BOOL)animated and present the action sheet.
3) When the action sheet is used, it calls it's delegate method - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex and within that method, dismiss it, or whatever else you need to do.
You would put all of these delegate methods in your original calling viewcontroller, conforming it to <UIAlertViewDelegate, UIActionSheetDelegate>.
You will need to make the original viewcontroller the delegate of the alert view AND the action sheet when you create them. I hope this makes sense.
More information http://developer.apple.com/library/ios/#documentation/uikit/reference/UIAlertView_Class/UIAlertView/UIAlertView.html
I would say it's probably b/c you are not resigning the first responder on the UITextField that is inside the UIAlertView. Make sure that before you show your UIActionSheet you do
[myTextField resignFirstResponder];

Dismiss the keyboard if action sheet is going to popup

I have a UIDatePickerView inside UIActionSheet as a input to the UITextField. When focusing on UITextField UIActionSheet will popup instead of Keyboard. When clicking on the done button in the UIActionSheet it'll hide. I have several other text fields behave as normal (Showing keyboard).
- (void)textFieldDidBeginEditing:(UITextField *)textField {
if ([textField isEqual:txtExpDate]) {
[textField resignFirstResponder];
[self showDatePicker];
}
}
- (void) showDatePicker{
UIActionSheet *datePickerActionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:nil destructiveButtonTitle:#"Done" otherButtonTitles:nil];
datePickerView = [[UIDatePicker alloc] initWithFrame:CGRectMake(0, 80, 0, 0)];
datePickerView.datePickerMode = UIDatePickerModeDate;
[datePickerActionSheet addSubview:datePickerView];
[datePickerActionSheet showInView:self.navigationController.view];
[datePickerActionSheet setBounds:CGRectMake(0, 0, 320, 500)];
[datePickerActionSheet release];
}
Now my problem is, Let say first user taps on normal textfield. Which will popups keyboard. Then without selecting done button he taps on date field. Which will popup action sheet (without dismissing the keyboard). After hiding actionsheet user has to tap on other text filed and click on return key of the keyboard.
I want to hide the keyboard if action sheet is going to popup?
Make the text field resignFirstResponder when you are going to bring up the action sheet. If you have more than one text fields, create an instance variable of type UITextField in .h file.
UITextField *currentTextField;
Then you can keep reference of the current text field in textFieldDidBeginEditing: method.
- (void)textFieldDidBeginEditing:(UITextField *)textField {
currentTextField = textField;
....
And, call [currentTextField resignFirstResponder] in showDatePicker method.
- (void)showDatePicker {
[currentTextField resignFirstResponder];
UIActionSheet *datePickerActionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:nil destructiveButtonTitle:#"Done" otherButtonTitles:nil];
...
EDIT: #Samuel Goodwin is correct. We don't have to track the text fields. We can simply do the following to dismiss the keyboard.
- (void)showDatePicker {
[self.view endEditing:YES];
...
If you want to dismiss the keyboard for any reason, simply do:
[[UIApplication sharedApplication] sendAction:#selector(resignFirstResponder) target:nil forEvent:nil];
Worked for all text fields everywhere anywhere.
The method proposed by EmptyStack did not work for me (using xcode 4.2). I placed the message [currentTextField resignFirstResponder] in the method willPresentActionSheet:(UIActionSheet *)actionSheet, that also didn't work. Then I put the message in actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex and that did the trick... strange!
I know this is a very old post but I just thought I'd add the solution that worked for me (the others didn't). I found that I had to re-assign a text field to be first responder the resign it again. It doesn't matter which text field becomes then looses first responder status, this is just a way to ditch that keyboard.
[self.textfield becomeFirstResponder];
[self.textfield resignFirstResponder];
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
if ([textField isEqual:txtExpDate]) {
[self showDatePicker];
return NO;
}
return YES;
}
It is working fine.
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
if(textField==myTextField2){
[myTextField1 resignFirstResponder];
[self showActionSheet];
return NO;
}
return YES;
}

UIAlertView choice causing resignFirstResponder to fail

I'm having a similar issue to Anthony Chan's question, and after trying every suggested solution, I'm still stuck.
Somehow, only after interacting with my UIAlertView, I'm unable to dismiss the keyboard in another view of my app. It's as though the Alert is breaking my UITextField's ability to resignFirstResponder. Below I instantiate my UIAlertView, which then calls its didDismissWIthButtonIndex method. Then, I call the showInfo method, which loads another UIViewController.
UIAlertView *emailFailAlert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"error message text."
delegate:self
cancelButtonTitle:#"Not now"
otherButtonTitles:#"Settings", nil];
[emailFailAlert setTag:2];
[emailFailAlert show];
[emailFailAlert release];
Once the 'Settings' option is pressed, I'm calling this method:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if ([alertView tag] == 2) {
if (buttonIndex == 1){
[self showInfo:nil];
}
}
}
My showInfo method loads the other ViewController, via the code below:
- (IBAction)showInfo:(id)sender {
FlipsideViewController *fscontroller = [[FlipsideViewController alloc] initWithNibName:#"FlipsideView" bundle:nil];
fscontroller.delegate = self;
fscontroller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:fscontroller animated:YES];
[fscontroller release];
}
Upon clicking any textField in this Flipside VC, I'm unable to dismiss the keyboard as I normally can with - (BOOL)textFieldShouldReturn:(UITextField *)textField, and [textField resignFirstResponder]. I've omitted this code bc this question is getting long, but I'm happy to post if necessary.
The interesting part is that if I comment out the [self showInfo:nil] call made when the button is clicked and call it by clicking a test button (outside the alertView didDismissWithButtonIndex: method), everything works fine. Any idea what's happening here?
Thanks in advance!
When an alert, with more than one dismissal option, is called above a keyboard - the keyboard becomes un-dismissible with resignFirstResponder on the active textfield;
You will need to dismiss the keyboard before showing the alert.
Assuming your UITextField is called myTextField;
[myTextField resignFirstResponder]; //That's the only line I added
UIAlertView *emailFailAlert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"error message text."
delegate:self
cancelButtonTitle:#"Not now"
otherButtonTitles:#"Settings", nil];
[emailFailAlert setTag:2];
[emailFailAlert show];
[emailFailAlert release];
I hope this helps anyone who had to deal with this oddly obscure issue.
You should not call alertView:didDismissWithButtonIndex: directly. This delegate method will be executed automatically in all cases after the alert has disappeared. Otherwise the code will be run twice!

Dismissing the keyboard for a UITextField in UIAlertView

I have a Customized UIAlertView.I have a UITextField in that AlertView, which becomes the firstResponder as soon as it is shown. Now I need to dismiss the keyboard for that UITextField when user touches somewhere else in the AlertView. I have touchesBegan event for that.
Everything is in place and works fine, except when I call resignFirstResponder on the UITextField, it resigns from being the first responder but the keyboard is not dismissed. Is there any way to dismiss that keyboard.
I was looking for the solutions and found a similar post here with no answers
If anyone was able to find a way out please let me know. I even tried the following way, but its not working
UIWindow* tempWindow;
// Because we cant get access to the UIKeyboard throught the SDK we will just use UIView.
// UIKeyboard is a subclass of UIView anyways
UIView* keyboard;
// Check each window in our application
for(int c = 0; c < [[[UIApplication sharedApplication] windows] count]; c ++)
{
// Get a reference of the current window
tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:c];
for(int i = 0; i < [tempWindow.subviews count]; i++)
{
// Get a reference to the current view
keyboard = [tempWindow.subviews objectAtIndex:i];
// Loop through all views in the current window
if([[keyboard description] hasPrefix:#"<UIKeyboard"] == YES){
[keyboard removeFromSuperview];
}
}
}
You must call resignFirstResponder on the UIAlertField,
eg:
[textField resignFirstResponder];[alertView resignFirstResponder];
Are you using custom code to show the keyboard? If not, try using the code from this StackOverflow question. I suspect that the resignFirstResponder message is not getting to the right control.
(The code in that answer is generally useful and sorely lacking from the SDK, IMHO.)
[self resignFirstResponder];
self means UIAlertView.
In my case it works.
There's no need to send a resignFirstResponder to the UIAlertView subclass. Just override becomeFirstResponder to return NO in UIAlertView subclass.
Apparently when you resign first responder on the UITextField, the UIAlertView becomes the next responder, keeping the Keyboard in place for some obscure reason.
Resigning that one too makes the keyboard disappear or better indeed override the becomeFirstResponder to return NO.
In my case this solution makes the UIAlertview animate to the upper left corner and animates back in place immediately after which looks very ugly and I cannot seem to find out why this is, but maybe someone else has some thoughts on that?
Here is my code extract in a class derived from UIAlertView:
- (BOOL)becomeFirstResponder
{
return NO;
}
- (void)someCustomButtonClicked:(id)sender
{
[textField resignFirstResponder];
// ...
}
-[UIView endEditing:]
http://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/UIView/UIView.html#//apple_ref/occ/instm/UIView/endEditing:
(Extra text because Stack Overflow requires at least 20 characters.)
Try these methods
-(void)viewDidLoad
{
[super viewDidLoad];
UIAlertView *loginAlert = [[UIAlertView alloc] initWithTitle:#"Welcome" message:#"Please Enter Login and Password" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[loginAlert setAlertViewStyle:UIAlertViewStyleLoginAndPasswordInput];
[loginAlert textFieldAtIndex:0].delegate = self;
[loginAlert textFieldAtIndex:1].delegate = self;
[loginAlert setTag:777];
[loginAlert show];
[loginAlert release];
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
And you should add the UITextFieldDelegate in the header file.
I met the problem just now.
Here's how I've solved the problem:
First, make your textField.delegate = self;
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Title" message:nil delegate:self cancelButtonTitle:#"cancel" otherButtonTitles:#"OK", nil, nil];
UITextField *oldPassWord = [alertView textFieldAtIndex:0];
oldPassWord.delegate =self;
UITextField *newPassWord = [alertView textFieldAtIndex:1];
newPassWord.delegate =self;
[alertView show];
Then, [textField resignFirstResponder];
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
[[alertView textFieldAtIndex:buttonIndex]resignFirstResponder];
}

Validating UITextField input and displaying error

What's the best practice for validating user input in a UITextField and displaying error? I tried performing checks inside textFieldShouldReturn and textFieldShouldEndEditing and popping up an error box when userever inputted invalid content.
However, during testing, the popup from textFieldShouldEndEditing is getting invoked multiple times. My simple test was to input invalid text in TextFieldA and navigate directly to TextFieldB. I observed 3 error popups, all generated by textFieldShouldReturn
It might be that by showing the alert, the edit field loses focus and thereby issues another textFieldShouldReturn or textFieldShouldEndEditing.
Try postponing the alert, by putting the code to show the alert into a separate method, and invoke that method from textFieldShouldEndEditing with [self performSelector:#selector(yourAlertMethod) withObject:nil afterDelay:0]
Actually, I found that I had a similar problem a while ago. The trick is to make sure that the text field doesn't have the focus any more for the duration that you show the alert.
Here's my solution:
// declare this in your interface as part of your ivars:
UITextField *currTextField;
// here comes the code to check for bad input and show an alert:
#pragma mark -
#pragma mark UITextFieldDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if ([self acceptsEntry:textField.text]) { // this checks if the text is valid
// We're done here
return YES;
} else {
// Setting the delegate to nil will prevent the textField to listen for Return key events.
textField.delegate = nil;
// Removing the observer will prevent the user from typing into the textfield using an external keyboard
[textField resignFirstResponder];
currTextField = textField;
UIAlertView *av = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"invalidEntry", #"")
message:NSLocalizedString(#"invalidEntryMessage", #"")
delegate:self
cancelButtonTitle:NSLocalizedString(#"OK", nil) otherButtonTitles:nil];
[av show];
[av release];
}
return NO;
}
#pragma mark -
#pragma mark UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
// gets called when the user dismisses the alert view
currTextField.text = #""; // erase bad entry
currTextField.delegate = self; // We want to listen for return events again
[currTextField becomeFirstResponder];
currTextField = nil;
}