How to dismiss keyboard for UITextView with return key?
IphoneObjective CKeyboardUitextviewIphone SoftkeyboardIphone Problem Overview
In IB's library, the introduction tells us that when the return key is pressed, the keyboard for UITextView
will disappear. But actually the return key can only act as '\n'.
I can add a button and use [txtView resignFirstResponder]
to hide the keyboard.
But is there a way to add the action for the return key in keyboard so that I needn't add UIButton
?
Iphone Solutions
Solution 1 - Iphone
Figured I would post the snippet right here instead:
Make sure you declare support for the UITextViewDelegate
protocol.
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if([text isEqualToString:@"\n"]) {
[textView resignFirstResponder];
return NO;
}
return YES;
}
Swift 4.0 update:
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text == "\n" {
textView.resignFirstResponder()
return false
}
return true
}
Solution 2 - Iphone
UITextView
does not have any methods which will be called when the user hits the return key. If you want the user to be able to add only one line of text, use a UITextField
. Hitting the return and hiding the keyboard for a UITextView
does not follow the interface guidelines.
Even then if you want to do this, implement the textView:shouldChangeTextInRange:replacementText:
method of UITextViewDelegate
and in that check if the replacement text is \n
, hide the keyboard.
There might be other ways but I am not aware of any.
Solution 3 - Iphone
I know this has been answered already but I don't really like using the string literal for the newline so here is what I did.
- (BOOL)textView:(UITextView *)txtView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if( [text rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]].location == NSNotFound ) {
return YES;
}
[txtView resignFirstResponder];
return NO;
}
Swift 4.0 update:
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (text as NSString).rangeOfCharacter(from: CharacterSet.newlines).location == NSNotFound {
return true
}
txtView.resignFirstResponder()
return false
}
Solution 4 - Iphone
I know this has been answered a lot of times, but here are my two cents to the issue.
I found the answers by samvermette and ribeto really useful, and also the comment by maxpower in the ribeto's answer. But there is a problem with those approaches. The problem that matt mentions in the samvermette's answer and it's that if the user wants to paste something with a line break inside it, the keyboard would hide without pasting anything.
So my approach is a mixture of the three above mentioned solutions and only checking if the string entered is a new line when the length of the string is 1 so we make sure the user is typing instead of pasting.
Here is what I have done:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
NSRange resultRange = [text rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet] options:NSBackwardsSearch];
if ([text length] == 1 && resultRange.location != NSNotFound) {
[textView resignFirstResponder];
return NO;
}
return YES;
}
Solution 5 - Iphone
A more elegant way is to dismiss the keyboard when the user taps somewhere outside of the keyboard's frame.
First, set your ViewController's view to the class "UIControl" in the identity inspector in UIBuilder. Control-drag the view into the ViewController's header file and link it as an action with the event as Touch Up Inside, such as:
ViewController.h
-(IBAction)dismissKeyboardOnTap:(id)sender;
In the main ViewController file, ViewController.m:
-(IBAction)dismissKeyboardOnTap:(id)sender
{
[[self view] endEditing:YES];
}
You can require a double tap or long touch using similar techniques. You may need to set your ViewController to be a UITextViewDelegate and connect the TextView to the ViewController. This method works for both UITextView and UITextField.
Source: Big Nerd Ranch
EDIT: I'd also like to add that if you are using a UIScrollView, the above technique may not work as easily through the Interface Builder. In that case, you could use a UIGestureRecognizer and call the [[self view] endEditing:YES] method within it instead. An example would be:
-(void)ViewDidLoad{
....
UITapGestureRecognizer *tapRec = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(tap:)];
[self.view addGestureRecognizer: tapRec];
....
}
-(void)tap:(UITapGestureRecognizer *)tapRec{
[[self view] endEditing: YES];
}
When the user taps outside of the keyboard and does not tap an entry space, the keyboard will dismiss.
Solution 6 - Iphone
Add this method in your view controller.
Swift:
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if text == "\n" {
textView.resignFirstResponder()
return false
}
return true
}
This method also can be helpful for you:
/**
Dismiss keyboard when tapped outside the keyboard or textView
:param: touches the touches
:param: event the related event
*/
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
if let touch = touches.anyObject() as? UITouch {
if touch.phase == UITouchPhase.Began {
textField?.resignFirstResponder()
}
}
}
Solution 7 - Iphone
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {
if([text isEqualToString:@"\n"])
[textView resignFirstResponder];
return YES;
}
yourtextView.delegate=self;
Also add UITextViewDelegate
Don't forget to confirm protocol
IF you didn't add if([text isEqualToString:@"\n"])
you can't edit
Solution 8 - Iphone
There is another solution while using with uitextview, You can add toolbar as InputAccessoryView in "textViewShouldBeginEditing", and from this toolbar's done button you can dismiss keyboard, the code for this is following:
> In viewDidLoad
toolBar = [[UIToolbar alloc]initWithFrame:CGRectMake(0, 0, 320, 44)]; //toolbar is uitoolbar object
toolBar.barStyle = UIBarStyleBlackOpaque;
UIBarButtonItem *btnDone = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(btnClickedDone:)];
[toolBar setItems:[NSArray arrayWithObject:btnDone]];
> In textviewdelegate method
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView
{
[textView setInputAccessoryView:toolBar];
return YES;
}
> In action of Button Done which is in toolbar is following:
-(IBAction)btnClickedDone:(id)sender
{
[self.view endEditing:YES];
}
Solution 9 - Iphone
I found the answer by [josebama][1] to be the most complete and clean answer available in this thread.
Below is the Swift 4 syntax for it:
func textView(_ textView: UITextView, shouldChangeTextIn _: NSRange, replacementText text: String) -> Bool {
let resultRange = text.rangeOfCharacter(from: CharacterSet.newlines, options: .backwards)
if text.count == 1 && resultRange != nil {
textView.resignFirstResponder()
// Do any additional stuff here
return false
}
return true
}
[1]: https://stackoverflow.com/a/23779209/5188399 "Josebama"
Solution 10 - Iphone
Simular to other answers using the UITextViewDelegate
but a newer swift interface isNewline
would be:
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if let character = text.first, character.isNewline {
textView.resignFirstResponder()
return false
}
return true
}
Solution 11 - Iphone
Using navigation controller to host a bar to dismiss the keyboard:
in the .h file:
UIBarButtonItem* dismissKeyboardButton;
in the .m file:
- (void)viewDidLoad {
dismissKeyboardButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismissKeyboard)];
}
-(void)textViewDidBeginEditing:(UITextView *)textView {
self.navigationItem.rightBarButtonItem = dismissKeyboardButton;
}
-(void)textFieldDidBeginEditing:(UITextField *)textField {
self.navigationItem.rightBarButtonItem = dismissKeyboardButton;
}
-(void)dismissKeyboard {
[self.textField resignFirstResponder];
[self.textView resignFirstResponder];
//or replace this with your regular right button
self.navigationItem.rightBarButtonItem = nil;
}
Solution 12 - Iphone
Add an observer in viewDidLoad
[[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(textViewKeyPressed:) name: UITextViewTextDidChangeNotification object: nil];
and then use the selector to check for "\n"
-(void) textViewKeyPressed: (NSNotification*) notification {
if ([[[notification object] text] hasSuffix:@"\n"])
{
[[notification object] resignFirstResponder];
}
}
It does use "\n" and not specifically check for a return key, but I think this is OK.
UPDATE
See ribto's answer below which uses [NSCharacterSet newlineCharacterSet]
in place of \n
Solution 13 - Iphone
swift
func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
if text == "\n" {
textView.resignFirstResponder()
}
return true
}
Solution 14 - Iphone
Just like matt comment to samvermette, I don't like the idea of detecting "\n" either. The "return" key is there for a reason in UITextView, that is to go to next line of course.
The best solution in my opinion is to mimic iPhone message app - which is to add toolbar (and button) on the keyboard.
I got code from following blog post:
http://www.iosdevnotes.com/2011/02/iphone-keyboard-toolbar/
Steps:
-Add toolbar to your XIB file - set the height to 460
-Add toolbar button item (if not already added). If you need to right-align it, also add flexible bar button item to XIB, and move the toolbar button item
-Create action that link your button item to resignFirstResponder as follow:
- (IBAction)hideKeyboard:(id)sender {
[yourUITextView resignFirstResponder];
}
-Then:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
CGRect frame = self.keyboardToolbar.frame;
frame.origin.y = self.view.frame.size.height - 260.0;
self.keyboardToolbar.frame = frame;
[UIView commitAnimations];
}
- (void)keyboardWillHide:(NSNotification *)notification {
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
CGRect frame = self.keyboardToolbar.frame;
frame.origin.y = self.view.frame.size.height;
self.keyboardToolbar.frame = frame;
[UIView commitAnimations];
}
Solution 15 - Iphone
Just solved this problem a different way.
-
Create a button that will be placed in the background
-
From the Attribute Inspector, change the button type to custom, and the makes the button transparent.
-
Expand the button to cover the whole view, and make sure the button is behind all the other object. Easy way to do this is to drag the button to the top of list view in the View
-
Control drag the button to the
viewController.h
file and create an action (Sent Event: Touch Up Inside) like :(IBAction)ExitKeyboard:(id)sender;
-
In
ViewController.m
should look like :(IBAction)ExitKeyboard:(id)sender { [self.view endEditing:TRUE]; }
-
Run app, and when you click away from the TextView, the keyboard disappears
Solution 16 - Iphone
Swift Code
Implement UITextViewDelegate in your class / View like so:
class MyClass: UITextViewDelegate { ...
set the textView delegate to self
myTextView.delegate = self
And then implement the following:
func textViewDidChange(_ textView: UITextView) {
if textView.text.characters.count >= 1 {
if let lastChar = textView.text.characters.last {
if(lastChar == "\n"){
textView.text = textView.text.substring(to: textView.text.index(before: textView.text.endIndex))
textView.resignFirstResponder()
}
}
}
}
EDIT I updated the code because it is never a good idea to change the user input in a textfield to for a workarround and not resetting the state after the hack code completed.
Solution 17 - Iphone
Try this :
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
if ([text isEqualToString:@"\n"]) {
[self.view endEditing:YES];
}
return YES;
}
Solution 18 - Iphone
//You can use this ...
Step 1. The first step is to make sure that you declare support for the UITextViewDelegate
protocol. This is done in your header file, as example here is the header called
EditorController.h:
@interface EditorController : UIViewController {
UITextView *messageTextView;
}
@property (nonatomic, retain) UITextView *messageTextView;
@end
Step 2. Next you will need to register the controller as the UITextView’s delegate. Continuing from the example above, here is how I have initialize the UITextView
with EditorController
as the delegate …
- (id) init {
if (self = [super init]) {
// define the area and location for the UITextView
CGRect tfFrame = CGRectMake(10, 10, 300, 100);
messageTextView = [[UITextView alloc] initWithFrame:tfFrame];
// make sure that it is editable
messageTextView.editable = YES;
// add the controller as the delegate
messageTextView.delegate = self;
}
Step 3. And now the final piece of the puzzle is to take action in response to the shouldCahngeTextInRange
message as follows:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range
replacementText:(NSString *)text
{
// Any new character added is passed in as the "text" parameter
if ([text isEqualToString:@"\n"]) {
// Be sure to test for equality using the "isEqualToString" message
[textView resignFirstResponder];
// Return FALSE so that the final '\n' character doesn't get added
return FALSE;
}
// For any other character return TRUE so that the text gets added to the view
return TRUE;
}
Solution 19 - Iphone
You can also hide keyboard when touch in view screen:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch * touch = [touches anyObject];
if(touch.phase == UITouchPhaseBegan) {
[txtDetail resignFirstResponder];
}
}
Solution 20 - Iphone
Swift answer:
override func viewDidLoad() {
super.viewDidLoad()
let tapGestureReconizer = UITapGestureRecognizer(target: self, action: "tap:")
view.addGestureRecognizer(tapGestureReconizer)
}
func tap(sender: UITapGestureRecognizer) {
view.endEditing(true)
}
Solution 21 - Iphone
I used this code to change responder.
- (BOOL)textView:(UITextView*) textView shouldChangeTextInRange: (NSRange) range replacementText: (NSString*) text
{
if ([text isEqualToString:@"\n"]) {
//[textView resignFirstResponder];
//return YES;
NSInteger nextTag = textView.tag + 1;
// Try to find next responder
UIResponder* nextResponder = [self.view viewWithTag:nextTag];
if (nextResponder) {
// Found next responder, so set it.
[nextResponder becomeFirstResponder];
} else {
// Not found, so remove keyboard.
[textView resignFirstResponder];
}
return NO;
return NO;
}
return YES;
}
Solution 22 - Iphone
The question asks how to do it with the return key but I think this could help someone with the intent to just make keyboard disappear when using UITextView:
private func addToolBarForTextView() {
let textViewToolbar: UIToolbar = UIToolbar()
textViewToolbar.barStyle = .default
textViewToolbar.items = [
UIBarButtonItem(title: "Cancel", style: .done,
target: self, action: #selector(cancelInput)),
UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
target: self, action: nil),
UIBarButtonItem(title: "Post Reply", style: .done,
target: self, action: #selector(doneInput))
]
textViewToolbar.sizeToFit()
yourTextView.inputAccessoryView = textViewToolbar
}
@objc func cancelInput() { print("cancel") }
@objc func doneInput() { print("done") }
override func viewDidLoad() {
super.viewDidLoad()
addToolBarForTextView()
}
Call addToolBarForTextView() in the viewDidLoad or some other life cycle method.
It seems that was the perfect solution for me.
Cheers,
Murat
Solution 23 - Iphone
Ok. Everyone has given answers with tricks but i think the right way to achieve this is by
Connecting the following action to the "Did End On Exit" event in Interface Builder
.
(right-click the TextField
and cntrl-drag from 'Did end on exit' to the following method.
-(IBAction)hideTheKeyboard:(id)sender
{
[self.view endEditing:TRUE];
}
Solution 24 - Iphone
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
if (range.length==0) {
if ([text isEqualToString:@"\n"]) {
[txtView resignFirstResponder];
if(textView.returnKeyType== UIReturnKeyGo){
[self PreviewLatter];
return NO;
}
return NO;
}
} return YES;
}
Solution 25 - Iphone
+ (void)addDoneButtonToControl:(id)txtFieldOrTextView
{
if([txtFieldOrTextView isKindOfClass:[UITextField class]])
{
txtFieldOrTextView = (UITextField *)txtFieldOrTextView;
}
else if([txtFieldOrTextView isKindOfClass:[UITextView class]])
{
txtFieldOrTextView = (UITextView *)txtFieldOrTextView;
}
UIToolbar* numberToolbar = [[UIToolbar alloc]initWithFrame:CGRectMake(0,
0,
[Global returnDeviceWidth],
50)];
numberToolbar.barStyle = UIBarStyleDefault;
UIBarButtonItem *btnDone = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"btn_return"]
style:UIBarButtonItemStyleBordered
target:txtFieldOrTextView
action:@selector(resignFirstResponder)];
numberToolbar.items = [NSArray arrayWithObjects:btnDone,nil];
[numberToolbar sizeToFit];
if([txtFieldOrTextView isKindOfClass:[UITextField class]])
{
((UITextField *)txtFieldOrTextView).inputAccessoryView = numberToolbar;
}
else if([txtFieldOrTextView isKindOfClass:[UITextView class]])
{
((UITextView *)txtFieldOrTextView).inputAccessoryView = numberToolbar;
}
}
Solution 26 - Iphone
You should add UIToolbar
to top UITextView to make easy rather than using shouldChangeTextIn
In Swift 4
let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
toolbar.barStyle = .default
toolbar.items = [
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(doneAction))
]
textView.inputAccessoryView = toolbar
@objc func doneAction(){
self.textView.resignFirstResponder()
}
Solution 27 - Iphone
I know it's not the exact answer to this question, but I found this thread after hunting the internet down for an answer. I assume others share that feeling.
This is my variance of the UITapGestureRecognizer which I find reliable and easy to use - just set the delegate of the TextView to the ViewController.
Instead of ViewDidLoad I add the UITapGestureRecognizer when the TextView becomes active for editing:
-(void)textViewDidBeginEditing:(UITextView *)textView{
_tapRec = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tap:)];
[self.view addGestureRecognizer: _tapRec];
NSLog(@"TextView Did begin");
}
When I tap outside the TextView, the view ends editing mode and the UITapGestureRecognizer removes itself so I can continue interacting with other controls in the view.
-(void)tap:(UITapGestureRecognizer *)tapRec{
[[self view] endEditing: YES];
[self.view removeGestureRecognizer:tapRec];
NSLog(@"Tap recognized, tapRec getting removed");
}
I hope this helps. It seems so obvious but I have never seen this solution anywhere on the web - am I doing something wrong?
Solution 28 - Iphone
Don't forget to set the delegate for the textView - otherwise resignfirstresponder won't work.
Solution 29 - Iphone
Try this .
NSInteger lengthOfText = [[textView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length];
Solution 30 - Iphone
For Xcode 6.4., Swift 1.2. :
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent)
{
super.touchesBegan(touches, withEvent: event)
if let touch = touches.first as? UITouch
{
self.meaningTextview.resignFirstResponder()
}
}
Solution 31 - Iphone
My hack for this :
1- create a button covering the whole view; 2- send it to the background of your view, 3- change it´s Type from "Round Rect" to "Custom" in the Attribute Inspector, 4- create an action 5- implement the action method:
- (IBAction)bgTouched:(id)sender
{
//to dismiss keyboard on bg btn pressed
[_userInput resignFirstResponder];
}
where _userInput is your TextField outlet
Solution 32 - Iphone
For Swift 3, this code allowed me to press outside of the UITextView to dismiss the keyboard.
@IBOutlet weak var comment: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
comment.delegate = self
let tapGestureRecogniser = UITapGestureRecognizer(target: self, action: #selector(tap))
view.addGestureRecognizer(tapGestureRecogniser)
}
func tap(sender: UITapGestureRecognizer) {
if comment.isFirstResponder {
comment.resignFirstResponder()
}
}
Solution 33 - Iphone
-(BOOL)textFieldShouldReturn:(UITextField *)textField; // called from textfield (keyboard)
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text; // good tester function - thanks
-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text; // good tester function - thanks
Solution 34 - Iphone
function to hideQueboard.
- (void)HideQueyboard
{
[[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil];
}