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

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!

Related

Snapshotting a view (_UIReplicantView) that has not been rendered at least once requires afterScreenUpdates:YES

I'm a new programmer that's still learning, so forgive me if my code looks like crap. I'm writing a program that has a ViewController called "infoViewController" with multiple UITextFields on it. The user is supposed to fill out the textfields and then click the "Next" button in the upper-right of the screen, which then activates a "Show" segue and takes them to the next VC. All the info from the Text Fields are stored in constants for later down the road when I take that information that they inputted and place it on a .PDF file that is generated at the end. I wanted the user to be able to easily go through the text fields, so I made it where when the user is on the TextField and clicks "Next" on their keyboard, it moves them to the next TextField so that they can fill them all out quickly and easily. The issue I run into is that for some reason I keep getting this error when I click "Next" on the keyboard:
[Snapshotting] Snapshotting a view (0x7f89955786c0, _UIReplicantView) that has not been rendered at least once requires afterScreenUpdates:YES.
The app still works... but I'd rather not have an error showing up in the panel (in Xcode) at all lol. Also, when it does this, it's like the screen glitches and flickers white for a split second (super fast) before it moves on to the next TextField... any solutions?
Please dumb anything you reply down to a beginners level. Thanks!
Here's the code I wrote for switching between TextFields:
extension infoViewController {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let nextTag = textField.tag + 1
if let nextResponder = textField.superview?.viewWithTag(nextTag) {
nextResponder.becomeFirstResponder()
} else {
textField.resignFirstResponder()
}
return true
}
}
I assigned the TextField delegates in "viewDidLoad."
(Ex: clientTextField.delegate = self)
I also assigned the tags in "viewDidLoad."
(Ex: clientTextField.tag = 0)
I had the same warning whenever a textField switched firstResponders. But this only happened for textFields that had the Text Input Trait of Capitalization set to anything other than None.
Text Input Traits can be found in the Attributes Inspector under Min Font Size.
In my case, changing Text Input Trait of Capitalization to None removed that warning. While this worked for me, I imagine this only solves a symptom, not the underlying problem.
I would say, wrap your call to
nextResponder.becomeFirstResponder()
And also your call to
textField.resignFirstResponder
In a very brief delay to let the function return before you play with the first responder.

Prevent keyboard from dismissing while changing views

On apps like say the stock messages app, if you’re in a conversation with the keyboard showing and swipe to go back to your conversation list, the keyboard remains up as the view gets swiped away.
I can’t seem to figure out how to mimic this behavior but I’ve seen it in other apps so it’s gotta be possible.
Edit: Not sure I understand why this is getting downvotes. It’s definitely a valid question where I couldn’t find the answer on google?
Edit 2: Here’s a video of what I’m trying to accomplish https://arxius.io/v/a555c8db compared to this behavior in discord https://arxius.io/v/0bfda09a
On apps like say the stock messages app, if you’re in a conversation with the keyboard showing and swipe to go back to your conversation list, the keyboard remains up as the view gets swiped away.
I don't actually see that behavior, at least in the Messages app on an iPhone. That said, the keyboard should remain visible whenever the first responder can accept text. If you want to switch to a different view controller and keep the keyboard visible, then make sure that first responder in the new view graph is editable. For example, if there's a text field, you could set it to be the first responder.
So before we talk about any kind of solution, let's learn exactly why the keyboard is dismissed.
As you know, iOS UI works on a view controller based system, with a text field ultimately being managed by a view controller somewhere.
When you click the back button, the active view controller is released from the navigation stack and therefore the system deduces that the text field is no longer in use, so it resigns it as the first responder.
Unfortunately there is no built in way to alter this behaviour and whilst we can speculate how apple might do it, we do not know.
The few options we have are not particularly neat or tidy, but a method I have had success with in the past is creating a hidden text field directly on the window before you pop the view controller and setting it first responder. Then on your other view controller, the text field can take first responder from the invisible one and remove it.
This is not ideal, but it's the only technique that comes to mind.
It is correct that you had to have UITextFiled available for both views to have keyboard passed between them consistently. But nobody said that this textfield had to be on screen, or had to be part of ViewController's view.
So all you had to do is to place this textFiled somewhere out of screen, for example as subview of your window, or NavigationController's view, make it first responder, and switch to your text field on view didAppear:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.textField.becomeFirstResponder()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
let fixedView = self.view.window
let fakeTextField = fixedView?.subviews.last as? UITextField ?? UITextField()
fakeTextField.autocapitalizationType = textField.autocapitalizationType
fakeTextField.autocorrectionType = textField.autocorrectionType
fakeTextField.center = CGPoint(x: -100, y: -100)
fixedView?.addSubview(fakeTextField)
fakeTextField.becomeFirstResponder()
}
Make sure that keyboard configuration is the same, otherwise you will have it switching.

UITextField endEditing:YES (force) Not Dismissing Keyboard

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.

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

iOS SDK: Dismiss keyboard when a button gets clicked?

How can I dismiss the keyboard when the user clicks a button?
A short example for a better understanding: the user edits some text in some textfields and at the end he doesn't click "Done" or smething else on the keyboard, but he clicks on an button "Save" while the keyboard is still shown. So, how can I now dismiss the keyboard?
Thx. Regards, Daniel
You can also call [view endEditing:YES] on any view above the text field in the hierarchy. This is useful if you aren't sure which text field has first responder status. It also optionally lets a text field delegate stop the action (by returning NO from shouldEndEditing:) which is nice if you are using a delegate to do validation on the fields.
in button action write,
if([textField isFirstResponder]){
[textField resignFirstResponder];
}
if there are more textfields get the current textfield reference everytime while editing started, and resign its responder in button action.
If you have multiple text fields and don't know which one is first responder (or you simply don't have access to the text fields from wherever you are writing this code) you can call endEditing: on the parent view containing the text fields.
In Swift, in the related view controller, add this line to the button function:
self.view endEditing(true)
In addition to SriPriya's answer, please have here the Swift 2 styled version:
if numberTextField.isFirstResponder() {
numberTextField.resignFirstResponder()
}
Please enjoy...

Resources