通过触摸UITableView的背景来关闭键盘

问题描述 投票:98回答:27

我有一个UITableViewUITextFields作为细胞。当触摸UITableView的背景时,我想解雇键盘。我试图通过创建一个UIButton大小的UITableView并将其放在UITableView后面来做到这一点。唯一的问题是,即使触摸在UITableView上,UIButton也能捕获所有触摸。我究竟做错了什么?

谢谢!

ios uitableview uikeyboard
27个回答
197
投票

这可以通过创建UITapGestureRecognizer对象轻松完成(默认情况下,这将在单击时检测“手势”,因此无需进一步自定义),指定触发手势时的目标/动作,然后附加手势识别器对象到你的表视图。

例如。也许在你的viewDidLoad方法:

UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard)];
[self.tableView addGestureRecognizer:gestureRecognizer];

hideKeyboard方法可能如下所示:

- (void) hideKeyboard {
    [textField1 resignFirstResponder];
    [textField2 resignFirstResponder];
    ...
    ...
}

请注意,触摸UITextField对象内部时不会触发手势。它虽然在UITableView背景,页脚视图,标题视图和UILabels内部细胞等被解雇。


6
投票

Swift 3版本没有阻止单元格上的点击。

viewDidLoad()方法:

let dismissKeyboardGesture = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard))
dismissKeyboardGesture.cancelsTouchesInView = false
tableView.addGestureRecognizer(dismissKeyboardGesture)

hideKeyboard看起来像这样:

func hideKeyboard() {
    view.endEditing(true)
}

5
投票

我有一个UITableViewController和实施touchesBegan:withEvent:不适合我。

这是有效的:

迅速:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    view.endEditing(true)
}

Objective-C的:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [self.view endEditing:YES];
}

4
投票
@interface DismissableUITableView : UITableView {
}
@end

@implementation DismissableUITableView

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
 [self.superview endEditing:YES];
 [super touchesBegan:touches withEvent:event];
}

@end

然后确保在您的Nib文件中将UITableView的类型设置为DismissableUITableView .....也许我可以想到这个类的更好的名称,但是你明白了。


4
投票

如果您的目标是iOS7,则可以使用以下方法之一:

tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;

tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;

前者将在滚动表格视图时将键盘设置为关闭屏幕,后者将像股票消息应用程序一样隐藏键盘。

请注意,这些来自UIScrollViewUITableView继承自 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView


2
投票

UITableView是UIScrollView的子类。

我这样做的方法是由用户监听滚动事件,然后是resignFirstResponder。这是在代码中实现的UIScrollViewDelegate方法;

<UITextFieldDelegate>

当我接近这些问题时,我发现最好的方法是研究每个对象和父类的委托协议(在本例中为UITableViewDelegate,UIScrollViewDelegate.NS对象触发的事件数量非常大且全面。它是也更容易实现协议然后继承任何东西。


2
投票

我有同样的问题,这是我的解决方案,它对我来说很完美:

在您实现UITableViewCell的视图或视图控制器中

(在我的情况下,我有一个名为TextFieldCell的自定义UITapGestureRecognizer),

@interface TextFieldCell : UITableViewCell <UITextFieldDelegate> { UITextField *theTextField; UITapGestureRecognizer *gestureRecognizer; } @property (nonatomic,retain) UITextField *theTextField; @property (nonatomic,retain) UITapGestureRecognizer *gestureRecognizer; 声明为属性:

self.gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(closeKeyboard:)];

并在您的视图/控制器中初始化它:

- (void)textFieldDidBeginEditing:(UITextField *)textField

superView方法中,使用addGestureRecognizer向上移动到tableView并调用[self.superview.superview addGestureRecognizer:gestureRecognizer];

- (void)textFieldDidEndEditing:(UITextField *)textField

[self.superview.superview removeGestureRecognizer:gestureRecognizer]; 中,只需删除手势识别器:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];
    if (selected)
    {
        [self.textField becomeFirstResponder];
    }
}

希望能帮助到你。


2
投票

我希望我的单元格在选择单元格的任何部分时打开键盘,如果单击单元格的任何位置,则关闭它。要打开键盘:

tableView:didSelectRowAtIndexPath:

(注意:我已经将细胞子类化了,但你可以在UITableView- (void)viewDidLoad { [super viewDidLoad]; //gesture recognizer to close the keyboard when user taps away UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard:)]; tap.cancelsTouchesInView = NO; [self.tableView addGestureRecognizer:tap]; } -(void)dismissKeyboard:(UIGestureRecognizer*)tapGestureRecognizer { if (!CGRectContainsPoint([self.tableView cellForRowAtIndexPath:[self.tableView indexPathForSelectedRow]].frame, [tapGestureRecognizer locationInView:self.tableView])) { [self.view endEditing:YES]; } } 委托方法中轻松实现这一点)

这样做意味着使用顶级解决方案,如果您单击两次单元格键盘会摇晃,首先手势识别器尝试关闭键盘,然后重新选择单元格并尝试打开键盘。

解决方法是检查当前所选单元格内是否发生了单击:

UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard)];
tapGestureRecognizer.cancelsTouchesInView = NO;
tapGestureRecognizer.delegate = self;
[self.suggestedTableView addGestureRecognizer:tapGestureRecognizer];
[tapGestureRecognizer release];

2
投票

我找到了一个效果很好的解决方案。

需要使用UIGestureRecognizerDelegate和方法 - gestureRecognizer:shouldReceiveTouch:。

将手势识别器添加到TableView,如下所示:

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if([touch.view isKindOfClass:[UITableViewCell class]]) {
        return NO;
    }
    // UITableViewCellContentView => UITableViewCell
    if([touch.view.superview isKindOfClass:[UITableViewCell class]]) {
        return NO;
    }
    // UITableViewCellContentView => UITableViewCellScrollView => UITableViewCell
    if([touch.view.superview.superview isKindOfClass:[UITableViewCell class]]) {
        return NO;
    }
    return YES; // handle the touch
}

- (void) hideKeyboard{
    [textField resignFirstResponder];
}

然后,实现shouldReceiveTouch委托方法以拒绝在UITableViewCell类中执行的触摸。只有在UITableViewCell类之外执行触摸时才会调用hideKeyboard方法。

http://82517.tumblr.com/post/13189719252/dismiss-keyboard-on-uitableview-non-cell-tap

1
投票

我正在寻找解决方案,并没有找到任何适合我的代码的东西,所以我这样做:

cancelsTouchesInView = NO

它基本上是前面提到的方法的组合,但不需要子类化任何东西或创建背景按钮。


1
投票

简单地使用UITapGestureRecognizer和// init UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapTableView:)]; tapRecognizer.cancelsTouchesInView = NO; [self.tableView addGestureRecognizer:tapRecognizer]; // Hide on tap - (void)didTapTableView:(UITapGestureRecognizer *)tap { CGPoint point = [tap locationInView:tap.view]; [self.view endEditing:!CGRectContainsPoint([self.tableView rectForRowAtIndexPath:[self.tableView indexPathForRowAtPoint:point]], point)]; } 意味着对单元格和UITextViews的点击也会触发隐藏。如果您有多个UITextView并且点击下一个UITextView,那就不好了。键盘将开始隐藏,然后下一个textView成为firstResponder,键盘再次可见。要避免这种情况,请检查分接位置,如果分接头不在单元格上,则仅隐藏键盘:

scrollViewWillBeginDragging:

为了触发scrollEnabled,tableView的YES属性必须是// Hide on scroll - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { [self.view endEditing:YES]; }

viewDidLoad(){

    let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))

    tableView.addGestureRecognizer(tap)

}

124
投票

如果设置:UITapGestureRecognizer解决方案适用于表格单元格选择:

gestureRecognizer.cancelsTouchesInView = NO;

1
投票

试试这个:

//Calls this function when the tap is recognized.
@objc func dismissKeyboard() {

    //Causes the view (or one of its embedded text fields) to resign the first responder status.
    view.endEditing(true)

}
something like this

0
投票

为什么要创建一个充满文本字段的表?您应该为包含文本字段的每一行使用详细视图。当您推送详细视图时,请确保调用“[myTextField becomeFirstResponder]”,以便用户只需单击一次表格列表即可开始编辑。


0
投票

如果你愿意为你的表视图创建子类(呃!),- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { BOOL backgroundTouched = YES; for (UITouch *touch in touches) { CGPoint location = [touch locationInView:self]; for (UITableViewCell *cell in self.visibleCells) { if (CGRectContainsPoint(cell.frame, location)) { backgroundTouched = NO; break; } } } if (backgroundTouched) { for (UITableViewCell *cell in self.visibleCells) { // This presumes the first subview is the text field you want to resign. [[cell.contentView.subviews objectAtIndex:0] resignFirstResponder]; } } [super touchesBegan:touches withEvent:event]; } 可能会工作:

- (BOOL)textFieldShouldReturn:(UITextField *)atextField
{
   [textField resignFirstresponder];
}

0
投票

如果你想在按下返回键的同时关闭键盘,你只需在textField中添加以下代码即可返回方法,即:

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                          action:@selector(dismissKeyboard)];

    [self.view addGestureRecognizer:tap];

一些文本字段可能有一个选择器视图或其他一些作为子视图,所以在这种情况下上面的方法不起作用,所以在这种情况下我们需要使用UITapGestureRecognizer类,即将以下代码片段添加到viewDidLoad方法,即:

-(void)dismissKeyboard 
{
    [textField resignFirstResponder];
}

现在只需将resign responder添加到选择器方法即:

   /**
     *  Shortcut for resigning all responders and pull-back the keyboard
     */
    -(void)hideKeyboard
    {
        //this convenience method on UITableView sends a nested message to all subviews, and they resign responders if they have hold of the keyboard
        [self.tableView endEditing:YES];

    }

希望它有所帮助,谢谢:)


0
投票

许多有趣的答案。我想在我认为最适合UITableView场景的解决方案中编译不同的方法(这是我经常使用的场景):我们通常想要的基本上是在两种情况下隐藏键盘:在文本UI元素外部点击,或者向下/向上滚动UITableView。我们可以通过TapGestureRecognizer轻松添加第一个场景,第二个场景通过UIScrollViewDelegate scrollViewWillBeginDragging:方法添加。第一项业务,隐藏键盘的方法:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    [self setupKeyboardDismissGestures];

}

- (void)setupKeyboardDismissGestures
{

//    Example for a swipe gesture recognizer. it was not set-up since we use scrollViewDelegate for dissmin-on-swiping, but it could be useful to keep in mind for views that do not inherit from UIScrollView
//    UISwipeGestureRecognizer *swipeUpGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard)];
//    swipeUpGestureRecognizer.cancelsTouchesInView = NO;
//    swipeUpGestureRecognizer.direction = UISwipeGestureRecognizerDirectionUp;
//    [self.tableView addGestureRecognizer:swipeUpGestureRecognizer];

    UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard)];
    //this prevents the gestureRecognizer to override other Taps, such as Cell Selection
    tapGestureRecognizer.cancelsTouchesInView = NO;
    [self.tableView addGestureRecognizer:tapGestureRecognizer];

}

此方法会在UITableView视图层次结构中重新分配子视图的任何textField UI,因此它比独立地重新分配每个元素更实用。

接下来,我们通过外部Tap手势解决问题:

@interface MyViewController : UIViewController <UIScrollViewDelegate>

将tapGestureRecognizer.cancelsTouchesInView设置为NO是为了避免gestureRecognizer覆盖UITableView的正常内部工作(例如,不干扰单元格选择)。

最后,要处理在向上/向下滚动UITableView时隐藏键盘,我们必须实现UIScrollViewDelegate协议scrollViewWillBeginDragging:方法,如下所示:

.h文件

#pragma mark - UIScrollViewDelegate

-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    [self hideKeyboard];
}

.m文件

static const CGFloat ANIMATION_DURATION = 0.4;
static const CGFloat LITTLE_SPACE = 5;
CGFloat animatedDistance;
CGSize keyboardSize;

@interface ViewController () <UITextFieldDelegate>
 @property (weak, nonatomic) IBOutlet UITextField *firstNameTXT;
  .....// some other text fields
 @property (weak, nonatomic) IBOutlet UITextField *emailTXT;
@end

@implementation ViewController
- (void)viewDidLoad{
.....
// add tap gesture to help in dismissing keyboard
UITapGestureRecognizer * tapGesture = [[UITapGestureRecognizer alloc]
                                       initWithTarget:self
                                       action:@selector(tapScreen:)];// outside textfields

[self.view addGestureRecognizer:tapGesture];

// set text fields return key type to Next, last text field to Done
[self.firstNameTXT setReturnKeyType:UIReturnKeyNext];
.....
[self.emailTXT setReturnKeyType:UIReturnKeyDone];

// set text fields tags
[self.firstNameTXT setTag:0];
....// more text fields
[self.emailTXT setTag:5];

// add keyboard notification
[[NSNotificationCenter defaultCenter] addObserver:self     selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
}
[[NSNotificationCenter defaultCenter] addObserver:self      selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
}

// dismiss keyboard when tap outside text fields
- (IBAction)tapScreen:(UITapGestureRecognizer *)sender {
  if([self.firstNameTXT isFirstResponder])[self.firstNameTXT resignFirstResponder];
  ...
  if([self.emailTXT isFirstResponder])[self.emailTXT  resignFirstResponder];

  }
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
   if(textField.returnKeyType==UIReturnKeyNext) {
     // find the text field with next tag
     UIView *next = [[textField superview] viewWithTag:textField.tag+1];
     [next becomeFirstResponder];
   } else if (textField.returnKeyType==UIReturnKeyDone || textField.returnKeyType==UIReturnKeyDefault) {
    [textField resignFirstResponder];
 }
return YES;
}

// Moving current text field above keyboard
-(BOOL) textFieldShouldBeginEditing:(UITextField*)textField{
   CGRect viewFrame = self.view.frame;
   CGRect textFieldRect = [self.view.window convertRect:textField.bounds fromView:textField];
   CGRect viewRect = [self.view.window convertRect:self.view.bounds fromView:self.view];
   CGFloat textFieldBottomLine = textFieldRect.origin.y + textFieldRect.size.height + LITTLE_SPACE;//

   CGFloat keyboardHeight = keyboardSize.height;

   BOOL isTextFieldHidden = textFieldBottomLine > (viewRect.size.height - keyboardHeight)? TRUE :FALSE;
  if (isTextFieldHidden) {
    animatedDistance = textFieldBottomLine - (viewRect.size.height - keyboardHeight) ;
    viewFrame.origin.y -= animatedDistance;
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:ANIMATION_DURATION];
    [self.view setFrame:viewFrame];
    [UIView commitAnimations];
  }
  return YES;
}

-(void) restoreViewFrameOrigionYToZero{
  CGRect viewFrame = self.view.frame;
  if (viewFrame.origin.y != 0) {
    viewFrame.origin.y = 0;
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [UIView setAnimationDuration:ANIMATION_DURATION];
    [self.view setFrame:viewFrame];
    [UIView commitAnimations];
  }
}

-(void)keyboardDidShow:(NSNotification*)aNotification{
   NSDictionary* info = [aNotification userInfo];
   keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
 }

-(void)keyboardDidHide:(NSNotification*)aNotification{
   [self restoreViewFrameOrigionYToZero];// keyboard is dismissed, restore frame view to its  zero origin
}
@end

我希望它有所帮助! =)


0
投票

这就是我最终的作品。我结合了不同答案的建议和代码。功能:关闭键盘,在编辑时移动键盘上方的文本字段,并设置“下一步”和“完成”键盘返回类型。更多字段的“REPLACE”...“

- (BOOL)findAndResignFirstResponder {
if (self.isFirstResponder) {
    [self resignFirstResponder];
    return YES;
}

    for (UIView *subView in self.subviews) {
        if ([subView findAndResignFirstResponder]) {
            return YES;
        }
    }
    return NO;
}

0
投票

@ mixca的答案非常有用,但是如果我和UITextField不同的话。我认为通过使用递归函数搜索主视图的所有子视图来处理它的最佳方法,请查看下面的示例

UITableView

而且你也可以将这个方法放到你的实用工具类中,并且可以使用@ mixca的答案等轻敲手势。


0
投票

backgroundView有一个方便的let tableBackTapRecognizer = UITapGestureRecognizer(target: self, action: #selector(hideKeyboard)) tableView.backgroundView = UIView() tableView.backgroundView?.addGestureRecognizer(tableBackTapRecognizer) 属性,我实现了这种行为,而没有搞乱细胞选择,如下面的Swift所示:

qazxswpoi

61
投票

这是最好的方法。就这样做吧

[self.view endEditing:YES];

要么

[[self.tableView superView] endEditing:YES];

52
投票

你也可以从Storyboard:enter image description here这样做


22
投票

由于UITableViewUIScrollView的子类,因此在下面实现一种委托方法提供了一种非常简单,快速的解决方案。甚至不需要涉及resignFirstResponder,因为视图层次结构内省并找到当前的响应者并要求它重新签署响应者状态。

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    [self.view endEditing:YES];
}

并记得将UIScrollViewDelegate添加到头文件。


13
投票

首先,通过添加scrollViewWillBeginDragging来听你的UIViewController中的UIScrollViewDelegate

在.h文件中:

@interface MyViewController : UIViewController <UIScrollViewDelegate> 

在.m文件中:

- (void)scrollViewWillBeginDragging:(UIScrollView *)activeScrollView {

    [self dismissKeyboard];

}

然后听其他互动:

- (void)setupKeyboardDismissTaps {

    UISwipeGestureRecognizer *swipeUpGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)];
    swipeUpGestureRecognizer.cancelsTouchesInView = NO;
    swipeUpGestureRecognizer.direction = UISwipeGestureRecognizerDirectionUp;
    [self.tableView addGestureRecognizer:swipeUpGestureRecognizer];

    UISwipeGestureRecognizer *swipeDownGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)];
    swipeDownGestureRecognizer.cancelsTouchesInView = NO;
    swipeDownGestureRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
    [self.tableView addGestureRecognizer:swipeDownGestureRecognizer];

    UISwipeGestureRecognizer *swipeLeftGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)];
    swipeLeftGestureRecognizer.cancelsTouchesInView = NO;
    swipeLeftGestureRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
    [self.tableView addGestureRecognizer:swipeLeftGestureRecognizer];

    UISwipeGestureRecognizer *swipeRightGestureRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)];
    swipeRightGestureRecognizer.cancelsTouchesInView = NO;
    swipeRightGestureRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
    [self.tableView addGestureRecognizer:swipeRightGestureRecognizer];


    UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissKeyboard)];
    tapGestureRecognizer.cancelsTouchesInView = NO;
    [self.tableView addGestureRecognizer:tapGestureRecognizer];

}

然后实现dismissKeyboard

- (void)dismissKeyboard {

    NSLog(@"dismissKeyboard");

    [yourTextFieldPointer resignFirstResponder];

}

如果像我一样,你想在自定义表格单元格中关闭UITextField的键盘:

- (void)dismissKeyboard {

    NSLog(@"dismissKeyboard");

    CustomCellClass *customCell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
    [customCell.textFieldInCell resignFirstResponder]; 

}

希望能帮助任何人搜索!!


9
投票
tableView.keyboardDismissMode = .onDrag

7
投票

我是这样做的:

在TableViewController中创建一个方法来停用第一个响应者(在那个时候是你的TextBox)

- (BOOL)findAndResignFirstResonder:(UIView *)stView {
    if (stView.isFirstResponder) {
        [stView resignFirstResponder];
        return YES;     
    }

    for (UIView *subView in stView.subviews) {
        if ([self findAndResignFirstResonder:subView]) {
            return YES;
        }
    }
    return NO;
}

tableView:didSelectRowAtIndexPath:中调用前一个方法:

- (void)tableView:(UITableView *)tableView
                             didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    ...
    [self findAndResignFirstResonder: self.view];
    ...
}

7
投票

这是您的编码乐趣的快速版本:

它添加了一个轻敲手势识别器然后解除键盘。不需要TextField的插座!

override func viewDidLoad() {
    super.viewDidLoad()
    view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "handleTap:"))
}

func handleTap(sender: UITapGestureRecognizer) {
    if sender.state == .Ended {
        view.endEditing(true)
    }
    sender.cancelsTouchesInView = false
}
© www.soinside.com 2019 - 2024. All rights reserved.