64

Alright, I have a couple of UITextFields and UITextViews inside a UIScrollView, and I'd like to set the keyboard to disappear whenever the scrollview is touched or scrolled (except when you touch down inside the text field/view, of course).

My current attempt at doing this is replacing the UIScrollView with a subclass, and setting it to call a removeKeyboard function (defined inside the main view controller) inside the touchesBegan method. However, this only removes the keyboard for a normal touch, not when the view is simply scrolled. So, what's the best way to remove the keyboard inside a UIScrollView?

Thanks in advance for your help.

Juan Boero
  • 6,281
  • 1
  • 44
  • 62
Nicholas1024
  • 880
  • 1
  • 9
  • 15

12 Answers12

145

Here is the cleanest way to achieve this in iOS 7.0 and above.

scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;

Or

scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;

In Swift:

scrollView.keyboardDismissMode = .onDrag

Or

scrollView.keyboardDismissMode = .interactive
Pei
  • 11,452
  • 5
  • 41
  • 45
  • can you give me the swift equivalent code? This dosent seem to work in swift. – Bala Vishnu Oct 27 '14 at 07:07
  • 1
    In swift, scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissMode.OnDrag; or scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissMode.Interactive; – Pei Oct 27 '14 at 08:44
  • I tried these two but didnt work. can you pls tell me where should I put these code? either in onTouchesBegan or viewDidLoad? And what is the difference between OnDrag and Interactive? Thanks for the response – Bala Vishnu Oct 27 '14 at 08:57
  • 1
    You can set it any time after controls/views are loaded. I'd recommend to put it in viewDidLoad and make sure that the scrollView is connected via IB. Even you can set it in IB. It's named Keyboard attribute under Scroll View section of Attribute inspector. – Pei Oct 27 '14 at 14:06
  • 4
    OnDrag means to dismiss the keyboard when you started dragging scroll view. Whereas Interactive means whenever you began touches in scroll view. – Pei Oct 27 '14 at 14:07
  • interactive means keyboard Disappears when ever we touch a white space? – Bala Vishnu Oct 27 '14 at 14:08
  • It's when you touch inside the scroll view. – Pei Oct 27 '14 at 14:17
  • 1
    Interactive doesnt trigger in Swift. – Philip Apr 17 '15 at 08:35
  • Add added this in IB. (Although does seem to work in Simulator). – wcochran Jul 14 '15 at 19:44
  • This is working for me however I am getting a warning in console: ```-[UIWindow endDisablingInterfaceAutorotationAnimated:] called on > without matching -beginDisablingInterfaceAutorotation. Ignoring.``` Has anyone else run into this? – Bueno Mar 21 '16 at 17:45
  • Will it work if there's a `view` inside the `scrollView` and all the `textfields` are inside the `view`? – iPeter Apr 19 '18 at 08:13
  • Yep, for sure. @iPeter – Pei Apr 19 '18 at 11:19
  • which one do you use .onDrag or .interactive? – Pei Apr 19 '18 at 11:38
  • Dismiss Interactively. – iPeter Apr 19 '18 at 11:57
  • Try onDrag then. Some people complained that interactive isn't working well. – Pei Apr 19 '18 at 14:22
  • If .interactively is not working - set "Bounce Vertically" to true in IB for the UIScrollView. This is because your content view is not big enough to allow scrolling. You need scrolling in order for it to work – Will Said Aug 27 '19 at 01:11
  • I found that if the Emoji keyboard is shown and you slide the emojis, this causes the keyboard to hide weirdly - any thoughts on how to fix that? – hyouuu Aug 10 '21 at 20:19
51

Bit late but if anyone else is searching an answer to this problem, this is how I have gone about solving it:

1) Create a tap gesture recognizer with a target callback method to dismiss your keyboard using resignFirstResponder on all your fields.

2) Add the tap gesture to the scrollview.

Here's an example:

UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard)];

// prevents the scroll view from swallowing up the touch event of child buttons
tapGesture.cancelsTouchesInView = NO;    

[pageScrollView addGestureRecognizer:tapGesture];

[tapGesture release];

...

// method to hide keyboard when user taps on a scrollview
-(void)hideKeyboard
{
    [myTextFieldInScrollView resignFirstResponder];
}
Zhang
  • 11,549
  • 7
  • 57
  • 87
39

Although the essence is the same, I prefer less code.

Setting the keyboard to disappear when the scrollView is scrolled in Attributes inspector:

make keyboard disappear when scrollView is scrolled

Then disappear keyboard when scrollView is tapped:

  1. Drag a Tap Gesture Recognizer onto your scrollView
  2. Ctrl-Drag
  3. Make an action
  4. Only one line in the action —— scrollView.endEditing(true). If you are using Objective-C, [self.scrollView endEditing: YES];
fujianjin6471
  • 5,168
  • 1
  • 36
  • 32
10

In Swift:

Bit late but if anyone else is searching an answer to this problem, this is how I have gone about solving it:

1) Create a tap gesture recognizer with a target callback method to dismiss your keyboard using resignFirstResponder on all your fields.

2) Add the tap gesture to the scrollview.

Here's an example:

import UIKit

class ViewController: UIViewController {

    @IBOutlet var t1: UITextField!
    @IBOutlet var t2: UITextField!
    @IBOutlet var t3: UITextField!
    @IBOutlet var t4: UITextField!

    @IBOutlet var srcScrollView: UIScrollView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "hideKeyboard")

        // prevents the scroll view from swallowing up the touch event of child buttons
        tapGesture.cancelsTouchesInView = false

        srcScrollView.addGestureRecognizer(tapGesture)
    }

    func hideKeyboard() {
        t1.resignFirstResponder()
        t2.resignFirstResponder()
        t3.resignFirstResponder()
        t4.resignFirstResponder()
    }
}
King-Wizard
  • 15,628
  • 6
  • 82
  • 76
9

Look at keyboardDismissMode property of UIScrollView.

// will hide keyboard when your text field is about to go beyond the keyboard.
vwScrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;

// will hide keyboard instantly once the scroll view started scrolling by user.
vwScrollView.keyboardDismissMode = UIScrollViewKeyboardDismissOnDrag;

// If you need to hide keyboard on tap of scroll view,consider adding a tap gesture or sub class and override touchesbegan: method.

Swift Version

vwScrollView.keyboardDismissMode = .interactive
vwScrollView.keyboardDismissMode = .onDrag
Naresh Reddy M
  • 1,096
  • 1
  • 10
  • 27
4

Try This

[self.selectedViewController.view endEditing:YES];
aturan23
  • 4,798
  • 4
  • 28
  • 52
Saad Ur Rehman
  • 798
  • 1
  • 10
  • 19
4

Create a extension class for hiding keyboard when touches scrollview/view anywhere

extension UIViewController {
  func hideKeyboardWhenTappedAround() {
    let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
    tap.cancelsTouchesInView = false
    view.addGestureRecognizer(tap)
  }
    
  @objc func dismissKeyboard() {
    view.endEditing(true)
  }
}

And call this method in viewDidLoad like

override func viewDidLoad() {
  super.viewDidLoad()
  self.hideKeyboardWhenTappedAround()    
}
aturan23
  • 4,798
  • 4
  • 28
  • 52
chandra1234
  • 357
  • 6
  • 15
3

A bit late but if anyone else is searching an answer to this problem with Swift 3:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    view.endEditing(true)
}
Jorge Casariego
  • 21,948
  • 6
  • 90
  • 97
2

When I added the gesture to a subclass of UIScrollView, I was having problems with the various gestures in my view tree interfering with each other, such as being able to click on subviews, scroll the view, and have the keyboard dismiss in all cases. I came up with this solution, which can be setup from a superclass of UIScrollView or from a UIViewController.

The DismissKeyboardTapGesture class uses ARC, works with any text fields under the view, and doesn't take over any clicks from subviews like buttons. Also takes advantage of iOS7 scrolling effect to dismiss keyboard.

Setting up from UISScrollView superclass:

    _dismissKeyboard = [[DismissKeyboardTapGesture alloc] initWithView:self];

or from UIViewController:

    _dismissKeyboard = [[DismissKeyboardTapGesture alloc] initWithView:self.view];

Here is the class:

@interface DismissKeyboardTapGesture : NSObject <UIGestureRecognizerDelegate>

@end

@implementation DismissKeyboardTapGesture

- (id)initWithView:(UIView *)view
{
    self = [super init];
    if (self) {
        UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTap:)];
        singleTap.cancelsTouchesInView = NO;
        singleTap.delegate = self;
        [view addGestureRecognizer:singleTap];

        if ([view respondsToSelector:@selector(setKeyboardDismissMode:)]) {
            // Bonus effect to dismiss keyboard by scrolling
            ((UIScrollView *)view).keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
        }
    }
    return self;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    // Don't stop any existing gestures in our view from working
    if (otherGestureRecognizer.view == gestureRecognizer.view) {
        return YES;
    }
    return NO;
}

- (void)singleTap:(UIGestureRecognizer*)gestureRecognizer
{
    // Close keyboard for any text edit views that are children of the main view
    [gestureRecognizer.view endEditing:YES];
}

@end
Skotch
  • 3,072
  • 2
  • 23
  • 43
1

Try this scroll view delegate method -

link delegate in IB to scroll view and then cop this code (modify as per your need).

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{         
//sample code    
    [challengeABallotComponent.voterNameTextField resignFirstResponder];
    [challengeABallotComponent.ballotNumberTextField resignFirstResponder];
    [locationInformation.pollingLocation resignFirstResponder];
}

This should work. You can try other delegate methods too like

   -(void)scrollViewDidScroll: (UIScrollView *)scrollView 
{
//do your stuff
}
user1140780
  • 988
  • 2
  • 13
  • 29
1
scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
FelixSFD
  • 6,052
  • 10
  • 43
  • 117
vignesh.P
  • 41
  • 9
1
    extension UIView{
    //Set tag via storyboard 
    func keyboardDissmissInteractiveMode(_ tag:Int){
        if let scrollView = self.viewWithTag(tag) as? UIScrollView{
            scrollView.keyboardDismissMode = .interactive
        }
        if let tableview = self.viewWithTag(tag) as? UITableView{
            tableview.keyboardDismissMode = .interactive
        }
    }

    func keyboardDissmissOnDragMode(_ tag:Int){
        if let scrollView = self.viewWithTag(tag) as? UIScrollView{
            scrollView.keyboardDismissMode = .onDrag
        }
        if let tableview = self.viewWithTag(tag) as? UITableView{
            tableview.keyboardDismissMode = .onDrag
        }
    }


    func keyboardDissmissInteractiveMode(_ view:UIView){
        if let scrollView = view as? UIScrollView{
            scrollView.keyboardDismissMode = .interactive
        }
        if let tableview = view as? UITableView{
            tableview.keyboardDismissMode = .interactive
        }
    }

    func keyboardDissmissOnDragMode(_ view:UIView){
        if let scrollView = view as? UIScrollView{
            scrollView.keyboardDismissMode = .onDrag
        }
        if let tableview = view as? UITableView{
            tableview.keyboardDismissMode = .onDrag
        }
    }
}