Prevent keyboard from dismissing while changing views - ios

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.

Related

Adding UITextField() to UI programmatically, cannot tap on field to get keyboard

I've been doing iOS development for a long time and I've never run into this before. The UITextField appears in the UI, it is correctly sized and placed by the constraints I've given it, but when I tap/click on it and doesn't respond. The cursor doesn't appear in the field and the keyboard doesn't show up.
let textfield = UITextField()
textfield.translatesAutoresizingMaskIntoConstraints = false
self.contentview.addSubview(textfield)
textfield.keyboardType = .numberPad
textfield.borderStyle = .roundedRect
textfield.text = "stuff"
// and then I set up constraints which are working as expected
I've double checked my simulator to make sure it isn't a soft keyboard issue.
I don't need any specialized UITextFieldDelegate behavior, I just need to standard behavior that tapping will cause it to become first responder, set the cursor there, and open the keyboard. When I add a UITextField via a storyboard, I don't need to set a delegate in order to get this behavior, so I can't imagine I would need to create specialized delegate code in order to get this basic behavior.
Just to test if something else was wrong, I've tried programmatically forcing the textfield to become first responder and that works.
I've tried forcing this to be true, just in case:
textfield.isUserInteractionEnabled = true
but that didn't change anything.
Any ideas? Why isn't my programmatically created UITextField accepting taps?
EDIT: Got an excellent suggestion to check out the Hierarchy Inspector to see if anything else is on top of it. It looks like I don't have anything on top of it: Here's a screenshot of the sim and of the hierarchy inspector turned a bit to the side so you can see the layers.
There isn't anything in front of the UITextField. (The UITextField has like 4 internal layers, but nothing is in front of them.)
As we have discussed in the comments under the OP, it's always a good idea to check the view hierarchy in the View Hierarchy Inspector to see if the frame is laid out properly and also if perhaps some other view isn't covering the other view which should become the first responder.
The view hierarchy inspector can be found here once the app is running on a simulator or a device.

Attaching a custom alert view above the iOS keyboard

I'm trying to design (a "properly designed," not "hack") custom alert view. The view should attach itself to the top of the keyboard; sliding up with the keyboard (if there is an alert) or being hidden (if there is no alert).
The view should always "stick" to the keyboard... including, for instance, when the keyboard hides. In that case, the view should slide right down, out of sight, along with the keyboard.
Here's an example of what I'm trying to achieve (with an active alert):
I have originally thought about subclassing UIAlertView, but it looks like that is not recommended. And, after experimenting a bit, this is clearly a tricky task. I've got an alert that shows up but, it turns into problems staying in sync with the keyboard, and I haven't found a way to make it "track" with the motion of the keyboard... not smoothly.
Any ideas?
You can achieve this with inputAccessoryView of UITextField and UITextView. See Custom Views for Data Input chapter in Apple's "Text Programming Guide for iOS" for more information.
For example, a very simple red bar above the keyboard can be added with the following code:
let keyboardAlertView = UIView(frame:CGRectMake(0,0,320,44))
keyboardAlertView.backgroundColor = UIColor.redColor()
textField.inputAccessoryView = keyboardAlertView

iOS keyboard flickers when switching view controllers

I have a registration form and I want to have the keyboard always on top.
The way I'm doing it now, is that when the user moves between view controllers, in viewDidLoad, the first UITextField becomes the first responder.
The problem is that the keyboard flickers (disappears and then appears again) when the user moves between view controllers.
Also, related to this: I have a form with a few uitextfields. When the user presses next it goes to the next uitextfield using becomefirstresponder. When the user is in the last textfield, the keyboard button becomes "Done". Then, when the user presses it, if there's an error with the last field, it should get the focus (calls becomeFirstResponder) but that doesn't happen (nothing get's the focus and the keyboard goes down). All the other fields get the focus fine, just this last field doesn't. I've tried about everything: switching to other textfields and back. The problem is that done automatically removes the keyboard.
You should have made two separate questions for this.
First, your flickering:
I'm guessing you're using a UINavigationController. You can add an invisible UITextField somewhere in the UINavigationController, which you give focus before you switch to a new ViewController. Then, when the new ViewController has appeared (viewDidAppear), set the focus to the first textField as you want.
However, the entire approach is kind of hackey and I don't recommend you use it. Instead, try using several views in a scrollView, of which you change the offset when you move to the new view. This will also solve the flickering.
Second, losing firstResponder status on Done:
The done button is specifically there to indicate exactly that which it says; Done. Pressing this assumes the user is finished and that no text is left to type, thus dismissing the keyboard.
If you really want to keep the Done button, then try the following;
Allow the user to dismiss the keyboard.
Upon dismissal, check for the error in the last field.
If there is an error, instead of calling [lastField becomeFirstResponder], try [self performSelector:#selector(thisSelectorWillCallFirstResponder) withObject:nil afterDelay:1.0].
In the method thisSelectorWillCallFirstResponder call [lastField becomeFirstResponder].
This will give time for the keyboard to disappear, before making it pop up again, so it doesn't interfere with the becomeFirstResponder call.
Another method would be to not use a Done button, but instead use the return key. You can intercept return anytime with the delegate method textFieldShouldReturn:. There you can handle any error checking, without causing the textField to lose its focus.

clearButton not working in UITextEditField

This is one of those "it was working a while ago" troubleshooting efforts.
I'm working on the document preview view controller, in which is a scroll view, which itself contains subclasses of UIView that represent each document. I'm modeling this pretty closely to how Keynote handles its document preview, except I build my scroll view horizontally and with paging. But the standard user experience is present: Long press on a document icon causes all document icons to start jiggling, nab bar has + button and Edit button, etc.
The issue at hand is that when you tap on the name of a document, I hide all the others, move the one being edited front and center, build a new text edit field, add it as a subview atop the real name label, and set it as first responder; but the
[editNameTextField setClearButtonMode:UITextFieldViewModeWhileEditing];
while correctly showing in the edit field is not taking any action when the user taps on the clear button.
I can't figure out what I may have done to cause this to not work -- it had been!
My first thought was that somehow my instance of this subclass is no longer the delegate for this text edit field. To try and confirm/deny that, I usurped a tap on the image view of the document preview to compare the delegate property to self, and it passes.
if (editNameTextField) {
NSLog(#"editNameTextField is still active");
if ([editNameTextField.delegate isEqual:self]) {
NSLog(#"we're still the delegate for the editNameTextField");
}
}
Editing the text within the edit field works fine. Pressing the Return/Done key correctly sends the delegate message textFieldShouldReturn:
While investigating this I implemented the delegate method textFieldShouldClear: just to write a log message if the method gets called (and return YES of course). It never gets called.
My next thought was that perhaps a subview had covered up the area where the clear button sits. So I implemented textFieldShouldBeginEditing: and used the opportunity to bring my the text field to the front. That didn't change anything either. I set a debugger breakpoint there to play a sound when it was called, and it got called, so I know my text edit field is frontmost.
I have only one troubleshooting strategy remaining: Go backwards through snap shots until it starts working again. Before doing that I thought I'd see if any of the more experienced folks out here have any suggestions of what to try next.
Where are you adding the textfield? As a subview of the scrollView? If you added the textfield and it is out of bounds of its parent view it won't receive any touches.
You can try and not call becomeFirstResponder and see if clicking it will show keyboard. Another possible error might be that the parent view of the UITextField has userInteractionEnabled = NO.
Without seeing more code I'm afraid I can not offer more solutions.

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!

Resources