UITextField endEditing:YES (force) Not Dismissing Keyboard - ios

I have several UITextField objects in a custom Date/Time editor control (no spinners). The control is in a UITableViewCell on a modally presented TableViewController during a data record add operation.
If users cancel the add operation I need to discard all the data and dismiss the controller and the keyboard if it is showing. I do this by calling this method when the Cancel button is pressed:
-(void)cancel
{
_tempMOC = nil;
[self.view endEditing:YES];
[self dismissViewControllerAnimated:YES completion:NULL];
}
According to the Apple docs, for UIView endEditing which state:
Specify YES to force the first responder to resign, regardless of whether it wants to do so.
my textFieldShouldEndEditing: return value should be ignored, however this is not the case.
I have verified with log statements that textFieldShouldEndEditing: is getting called.
But, the value is not ignored because only when this method returns YES is the keyboard dismissed.
The result is that the modal view controller gets dismissed leaving an orphaned keyboard on the screen which can only be gotten rid of by restarting the app.
This is a problematic pain in the arse because I do various validation operations in here:
-(BOOL)textFieldShouldEndEditing:(UITextField *)tf
{
/* Validate data for up to 6 UITextFields in this control. */
...
return valid ? YES : NO;
}
In the case where the user hit cancel on the controller, I really need it to ignore the return value, else I have have to do some kind of stupid caveat to figure out that six levels up somewhere the user hit cancel and in that case always return YES.
Any thoughts on what might be going on or if I have missed something... or ugh, I've found yet another Apple bug?!
****** UPDATE ****
This is now reported as radar: 32442632.

Related

Can't get UILabel to display from boolean condition

I can't for the life of me figure out why this won't work. I'm trying to display a label after determining a boolean condition. No matter what I do, the label won't display. So in a pathetic desperate attempt, I put the code to display the label inside a button event to force it to display. Now it works perfectly (from the button click). But it still won't work from the boolean condition!
This is in viewDidLoad (note that the 'true' is just to isolate where the fault is):
if (true) [self showSubscribeLabel];
This is from the button click:
- (IBAction)askUserToSubscribe:(id)sender {
[self showSubscribeLabel];
}
-(void) showSubscribeLabel {
NSLog(#"here");
self.subscribeLabel.hidden = NO;
[self.view bringSubviewToFront:self.viewUnauthorized];
}
I see the output here when the controller loads and then again after clicking the button. What am I doing wrong? Thanks!
You should move your test and method invocation to viewWillAppear - often things aren't fully initialised in viewDidLoad. Having the code in viewWillAppear will also ensure that the condition is evaluated every time the view controller appears (such as when another view pops off a navigation stack) rather than when it is loaded for the first time.

Is there a way to know that iOS keyboard is really hidden (dismissed by user)?

I need a way to detect the case when user dismisses iOS keyboard manually, using "keyboard" button on keyboard. I tried to use UIKeyboardDidHideNotification, but quickly discovered that this event is also fired when user splits the keyboard, leaving it on screen.
Is there a way to know for sure that keyboard was really hidden?
To get solution I had to slightly modify my original implementation: I've replaced assigning nil to inputView member of my main view with creating and destroying custom invisible UIView<UIKeyInput> view to show and hide keyboard correspondingly. This allowed me to override this view's resignFirstResponder method which is always called on keyboard resigning - either in normal or in split state, when user dismisses keyboard using special button or when I remove it programmatically.
I believe that UIKeyboardDidHideNotification is only sent when the keyboard is truly gone. From the Apple docs:
Posted immediately after the dismissal of the keyboard.
However, you could also look to see if any of your inputs are currently the first responder:
BOOL keyboardUp = NO;
for (UIView *view in self.textInputs)
{
if (view.isFirstResponder)
{
keyboardUp = YES;
break;
}
}

Problems with UITextField & Keyboard Management in iOS

(I'm targetting iOS 2.0 for the time being.)
I have a class which subclasses UITextField and implements the UITextFieldDelegate protocol.
At first, I did not set the class as its own delegate, and when I clicked in the field, the keyboard popped up automatically. So far, so good.
I send the setReturnKeyType: message with UIReturnKeyDone, so it changes the bottom-right button to say Done.
The problem was, when I clicked Done or pressed [Return], nothing happened - the keyboard wouldn't go away.
I tried adding self as an observer for the end editing notification, but it never got called when Done was clicked; from a Google search it seems that only gets fired when the field resigns as the first responder - which is the bit I can't get to happen.
I then found answers on here that suggested adding self as the delegate and handling textFieldShouldReturn:. The problem is, as soon as I add self as the delegate, the keyboard no longer pops up when you click the field (and it doesn't gain focus) - it seems it isn't becoming first responder.
I tried handling textFieldShouldBeginEditing: and returning YES (docs say that should be the default if not overridden), and that got called, but made no difference. In my ignorance I tried [textField becomeFirstResponder] and was rewarded with a stack overflow (now I know that is called in response to trying to become first responder).
I'm now thoroughly stuck!
Can anyone help? I just want the keyboard to go away when the user clicks Done or presses [Return].
Are you using an xib or doing it programatically? If you are using an xib then you may have forgotten to connect the delegate in File's Owner.
(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
Try This one.
My fault. Turns out you're not supposed to set a text field's delegate to itself - there are complications due to unhandled selectors being forwarded to its delegate. If it doesn't respond to a selector the message goes to its delegate... which is itself, which we already know doesn't respond. Hence, an infinite loop.
Using a separate object as the delegate worked perfectly.
Hopefully this will help someone else avoid the same problem.

UITextField's keyboard won't dismiss. No, really

I have a table view in a modal form sheet (iPad), and one of the cells contains a UITextField. My view controller holds a reference to the text field and is also its delegate.
When the text field hits Return, I tell it to -resignFirstResponder inside of -textFieldShouldReturn:. In another case, I want to force it to end editing, so I tell the whole table view to -endEditing:YES. Afterwards I release my local reference to the text field, and reload the row to replace it with something else.
The keyboard won't go away in either case. I don't know what's wrong, and I'm not sure how to debug further. I've never had a sticky keyboard problem with any other text editing I've done-- the firstResponder resignation has always behaved as expected.
Any thoughts? Thanks.
The Apple docs describe this exception:
On the iPad, if a view controller modally presents its view using the
"form sheet" style, the keyboard, once shown, is not hidden until the
user taps the dismiss key or the modal view controller is
programmatically dismissed. The purpose of this behavior is to avoid
excessive animations as a user moves between views that are largely,
but not entirely, text fields.
Which happens to apply here (modal form sheet on iPad). It's apparently just not possible to dismiss the keyboard in this case. Super. :\
Implement -disablesAutomaticKeyboardDismissal and return NO. It does work on iOS 6, but you have to implement it in the right controller. If you have a modal form sheet navigation controller with a child controller that has text fields, it's the navigation controller that needs the method implementation, not the child.
(See also Dismiss keyboard on IPAD)
Since the disablesAutomaticKeyboardDismissal override isn't working on iOS6, I had to connect each text field's "Did End On Exit" event to a method and then dismiss the keyboard there, like so:
- (IBAction)doneEditing:(id)sender {
[sender endEditing:YES];
}
The disablesAutomaticKeyboardDismissal refused to work for me on iOS 7.
But... I managed to solve this issue by simply disabling the UITextFields on the screen.
My solution is described here.
This even works on Modal UIViewControllers.
I just found a unique situation where this occurs. I have a view that when dismissed leaves the keyboard up on the screen. I checked everything, my UITextFields delegates were connected to my view, etc. Trying to dismiss the keyboard manually in viewWillDisappear() wouldn't work, either by resignFirstResponder() on the fields or endEditing() on the view.
Then I realized it was my field validation code in the delegate methods themselves. Every time editing ended in a field I validate the text in textFieldShouldEndEditing delegate method to ensure the text is reasonable, much like this, and don't allow them to tab out of the field until it is validated.
func textFieldShouldEndEditing(textField: UITextField) -> Bool
{
if self.validateField(textField) {
return true
} else {
return false
}
}
So when a user returned to the previous view without entering any text, the validation fails, the text field is not allowed to relinquish it's first responder status, and the keyboard remains on screen as they return to the previous view.
It's a unusual situation but hopefully this will help someone else who runs into it.
In swift just give your UITextField a delegate and generate textFieldShouldReturn(), see below for example -
class ViewController: UIViewController, UITextFieldDelegate {
func settingUpTextField() {
textField.delegate = self;
return;
}
...
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder();
return true;
}
}
done!

How to prevent multiple UIAlertView from stacking up?

I'm using MPMoviePlayerController in iOS. I'm listening on any errors it may have while playing videos. In my error handlers, I pop up an UIAlertView. Sometimes errors may occur in quick succession of each other and thus multiple alert boxes will stack up. For a better user experience, I wish to not pop up another alert if an earlier one is still displayed.
Try this:
Set a boolean to true when you pop up an alert, set it to false when you close an alert, and always check the boolean to see if it's true before you pop up an event. If it is true, you'll know you have an alert window already showing.
You can find this solution and some other discussion here.
You can implement this yourself trivially. Since you're displaying the alert, and you're also the alert's delegate so you know when it's gone, you can easily track whether there's an alert visible just by setting a boolean flag upon alert show and alert hide. That way if the boolean is set, you can quash any subsequent alerts.
When an alert appears, it will be moved to a _UIAlertOverlayWindow. Therefore, a pretty fragile method is to iterate through all windows and check if there's any UIAlertView subviews.
-(BOOL)checkAlertViewVisibleStatus
{
for (UIWindow* window in [UIApplication sharedApplication].windows)
{
NSArray* subviews = window.subviews;
if ([subviews count] > 0)
if ([[subviews objectAtIndex:0] isKindOfClass:[UIAlertView class]])
return YES;
}
return NO;
}
This is undocumented as it depends on internal view hierarchy, although Apple cannot complain about this. A more reliable but even more undocumented method is to check if `
[_UIAlertManager visibleAlert]
` is nil.
These methods can't check if a UIAlertView from SpringBoard is shown.
As far as I know, the only way is to keep track of whether or not an alert is currently being displayed and/or one is currently being dismissed within your application. Try showing the alert in the appDelegate, and then using a notification to notify the appDelegate each time the alert is closed. This way the appDelegate keeps track of whether or not there is an alert with a boolean flag variable.
Use the new UIAlertViewController. If you try to present an alert while another is in view it ignores it and outputs the warning shown below. It's a nasty side affect for people who want the traditional behaviour of stacked alerts but for your case it's a good solution.
Warning: Attempt to present <UIAlertController: 0x7f9ef34c17e0> on <MasterViewController: 0x7f9ef344ec90> which is already presenting (null)
It should work:
-(BOOL) doesAlertViewExist
{
if ([[UIApplication sharedApplication].keyWindow isMemberOfClass:[UIWindow class]])
{
return NO;//AlertView does not exist on current window
}
return YES;//AlertView exist on current window
}

Resources