399

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?

GabrieleMartini
  • 1,665
  • 2
  • 19
  • 26
Chilly Zhong
  • 16,763
  • 23
  • 77
  • 103
  • Follow instructions on this blog post: http://iphonedevelopertips.com/cocoa/how-to-dismiss-the-keyboard-when-using-a-uitextview.html – ManojN Sep 02 '09 at 07:10

34 Answers34

524

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
}
phatmann
  • 18,161
  • 7
  • 61
  • 51
samvermette
  • 40,269
  • 27
  • 112
  • 144
  • 80
    The trouble is that this is not really a way of detecting that the user has tapped the return *key*. The user might *paste* a return character into the text field and you'd get the same delegate message. Basically you're misusing the text view. The way to dismiss the keyboard is (1) not to do so at all (as when composing a message in the Mail app) or (2) to have a Done button elsewhere in the interface (as in the Notes app). You could attach such a button as an accessory view to the keyboard, for example. – matt Dec 23 '10 at 18:18
  • @ Sam V that snippet was cool man. it works for me. thanks a ton man. Is there any snippet for dissmisal of numeric keyboard. regards shishir – Shishir.bobby Jan 12 '11 at 17:29
  • Magnificent. Just a reminder that in some situations, to get rid of the keyboard, try along the lines... [self.view endEditing:YES]; – Fattie Dec 08 '13 at 20:49
  • @Vojto, works fine on iPad. You have probably not set the delegate. – Vincent Aug 26 '14 at 17:50
  • 4
    The issue is that the keyboard may be hiding part of the interface that is used to dismiss. It is really absurd that IOS doesnt have a button on every keyboard dedicated to dismiss the keyboard so the user can do so if he wishes. – Sani Elfishawy Nov 19 '14 at 16:44
  • Also when a return button is changed to a done button. It should act like a done button and not a newline button. – Sani Elfishawy Nov 19 '14 at 16:45
  • Does it work on emulator? Mine already set with delegate but doesnt work well on emulator. – Sonic Master Nov 05 '15 at 02:30
367

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.

Venk
  • 5,949
  • 9
  • 41
  • 52
lostInTransit
  • 70,519
  • 61
  • 198
  • 274
  • 1
    Thanks and the method works well. The reason to use UITextView is that it can hold text in multiple lines. And now I'm using it as a message box. – Chilly Zhong Apr 01 '09 at 06:12
  • 28
    It is easy enough to change the return key to "done" using either `[textField setReturnKeyType: UIReturnKeyDone];` or using interface builder – Casebash Sep 28 '10 at 02:58
  • 9
    Okay, I now understand that the Apple way of finishing with a multiline text field is to add done to the menu bar – Casebash Sep 28 '10 at 03:09
  • See below; use the UITextViewDelegate method textView:shouldChangeTextInRange:replacementText:. It works great. – Greg Maletic Jun 03 '11 at 00:37
  • 8
    This is not a good way to resolve this because you're restricting user to use enter to go out from keyboard. Probably the best way is adding a button that execute resignFirstResponder method. – Ele Nov 07 '13 at 21:56
  • 1
    Try using the UIKeyboard accessory view and adding a button that resigns the keyboard. – Braydon Batungbacal Nov 09 '13 at 14:18
  • 5
    @Casebash. It seems that setting the return key to "done" doesn't solve the problem in Xcode 6.4 , Swift 2.0. I set the key using IB. – MB_iOSDeveloper Jul 09 '15 at 07:20
  • Thank you! Though departing from HIG this fits my usecase perfectly because the TextView had to 1) be able to show wrapped text 2) not allow multi-line text to be entered. – user3099609 Nov 27 '15 at 17:04
85

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
}
Joseph Francis
  • 1,111
  • 1
  • 15
  • 26
ribeto
  • 2,038
  • 1
  • 16
  • 17
  • 6
    I might modify it like this: NSRange resultRange = [text rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet] options:NSBackwardsSearch]; Because this is a hack anyway, it seems checking the end of the string for a return might be a safer route. – maxpower May 07 '13 at 14:50
  • @maxpower Very good comment. Also, it's better to check against replaced text, e.g. `NSString *replacedText = [textView.text stringByReplacingCharactersInRange:range withString:text]`. – Rudolf Adamkovič Jan 06 '14 at 10:55
  • Imagine a user pasting some text copied from elsewhere that *contains* a newline. They would probably be confused when instead of appending the text the app just dismisses the keyboard. – Nikolai Ruhe Oct 12 '15 at 08:04
44

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;
}
Community
  • 1
  • 1
josebama
  • 848
  • 8
  • 11
  • This is a best solution for this problem. samvermette answer doesn't account the situation where the user wants to paste a text. – marcopaivaf Nov 19 '14 at 19:55
37

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.

TMilligan
  • 3,520
  • 1
  • 18
  • 12
  • 1
    I like idea with `GestureRecognizer` but huge problem is that all buttons or control on the view are no longer clickable. – expert Sep 03 '13 at 09:08
35

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()
        }
    }
}
swiftBoy
  • 35,607
  • 26
  • 136
  • 135
Alexander Volkov
  • 7,904
  • 1
  • 47
  • 44
25
-(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

David
  • 3,285
  • 1
  • 37
  • 54
Vineesh TP
  • 7,755
  • 12
  • 66
  • 130
  • 2
    -1 This is just a poorer version of samvermette's answer. You missed returning `NO` if text is equal to `@"\n"`. – devios1 Oct 22 '13 at 00:49
20

I found the answer by josebama 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
}
Lou Zell
  • 5,255
  • 3
  • 28
  • 23
Voxel Minds
  • 375
  • 2
  • 5
  • The `resultRange` aims to test whether the text only contains newlines that avoids hard-code "\n". – Qinghua Aug 14 '17 at 03:38
19

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
}
Seamus
  • 1,107
  • 10
  • 22
  • You can add additional functionality you want when the return button is clicked after the line of code: textView.resignFirstResponder() – NhlanhlaNkosi Feb 22 '23 at 14:12
17

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];
}
swiftBoy
  • 35,607
  • 26
  • 136
  • 135
g212gs
  • 863
  • 10
  • 26
6

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

ToddB
  • 2,490
  • 3
  • 23
  • 40
  • Err it does use `\n` and return key is detected based on `\n` so it does check for return key. The only difference is that you are using notifications rather than using the textViewDelegates. – GoodSp33d Jan 26 '15 at 14:57
  • Now I think checking for `[NSCharacterSet newlineCharacterSet]` rather than \n might be a better way to go. – ToddB Jan 26 '15 at 18:34
6

swift

func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
    if text == "\n" {
        textView.resignFirstResponder()
    }
    return true
}

and configure

6

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;
}
David
  • 3,285
  • 1
  • 37
  • 54
Alex Stone
  • 46,408
  • 55
  • 231
  • 407
5

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
Sparkup
  • 3,686
  • 2
  • 36
  • 50
Uvichy
  • 57
  • 1
  • 1
  • You have to also add : -(void)textViewDidEndEditing:(UITextView *)textView { [self.TextView resignFirstResponder]; } – samouray Jul 15 '15 at 08:28
5

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.

Joyful Machines
  • 120
  • 2
  • 8
Frithjof Schaefer
  • 1,135
  • 11
  • 22
5

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];
}
friend
  • 1,909
  • 2
  • 21
  • 28
  • Off topic. While your solution is elegant, it does not respond to the original question: "How to dismiss keyboard for UITextView with return key?". There are situations when UITextView is employed to simulate a word wrapping UITextField, not to enter multiple lines. – SwiftArchitect Jul 16 '12 at 10:51
  • 2
    Although it is off topic, it is very useful. I also want a UITextView to be entered with multiple line and dismiss keyboard when I want to. – Yeung Oct 04 '12 at 04:37
4

Try this :

 - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
    if ([text isEqualToString:@"\n"]) {
        [self.view endEditing:YES];
    }

    return YES;

}
Shaik Riyaz
  • 11,204
  • 7
  • 53
  • 70
Nabil El
  • 972
  • 1
  • 10
  • 24
4

//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;
}
benka
  • 4,732
  • 35
  • 47
  • 58
3

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];
      }
 }
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
Hitesh Vaghela
  • 1,635
  • 1
  • 11
  • 11
3

Swift answer:

override func viewDidLoad() {
    super.viewDidLoad()
    let tapGestureReconizer = UITapGestureRecognizer(target: self, action: "tap:")
    view.addGestureRecognizer(tapGestureReconizer)
}

func tap(sender: UITapGestureRecognizer) {
    view.endEditing(true)
}
Glauco Neves
  • 3,483
  • 1
  • 24
  • 36
3

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;

    }
Avijit Nagare
  • 8,482
  • 7
  • 39
  • 68
3

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

Fattie
  • 27,874
  • 70
  • 431
  • 719
Murat Yasar
  • 994
  • 9
  • 24
2

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];
}
carbonr
  • 6,049
  • 5
  • 46
  • 73
1
- (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;
}
Rinju Jain
  • 1,694
  • 1
  • 14
  • 23
1
+ (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;
    }
}
Venu Gopal Tewari
  • 5,672
  • 42
  • 41
1

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()
}
Giang
  • 2,384
  • 2
  • 25
  • 26
  • By far the best solution. Note that instead of setting the frame, just use `toolbar.sizeToFit()` – Fattie Nov 23 '19 at 18:57
0

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?

BGC
  • 25
  • 5
0

Don't forget to set the delegate for the textView - otherwise resignfirstresponder won't work.

0

Try this .

NSInteger lengthOfText = [[textView.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length];
Hiren Panchal
  • 2,963
  • 1
  • 25
  • 21
0

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()
        }
    }
MB_iOSDeveloper
  • 4,178
  • 4
  • 24
  • 36
-1

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

LAOMUSIC ARTS
  • 642
  • 2
  • 10
  • 15
-1

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()
        }
    }
iCyberPaul
  • 650
  • 4
  • 15
-2

function to hideQueboard.

- (void)HideQueyboard
{
    [[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder)   to:nil from:nil forEvent:nil];
}
Alan10977
  • 973
  • 1
  • 9
  • 7
-2
-(BOOL)textFieldShouldReturn:(UITextField *)textField; // called from textfield (keyboard)

-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text; // good tester function - thanks
SNR
  • 1,249
  • 2
  • 20
  • 38
Josh
  • 29
  • 1