当失去UITextView焦点时隐藏键盘

46

我有一个UITextView,用于让用户提交一些文本。

我的问题是,我似乎无法想出如何让用户通过点击UITextView外部来“取消”。

10个回答

107

简化自tuzzolotron的回答: 在您的xib中正确连接以下outlet

    IBOutlet UITextView *myTextView;

在视图控制器中使用以下代码:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    UITouch *touch = [[event allTouches] anyObject];
    if ([myTextView isFirstResponder] && [touch view] != myTextView) {
        [myTextView resignFirstResponder];
    }
    [super touchesBegan:touches withEvent:event];
}

点击视图控制器视图上文本视图之外的任何位置,会注销第一响应者并关闭键盘。


如果这对downvoter没有用,我很乐意提供帮助。只需留下评论或发起一个新问题,并在此处链接即可。 - ohhorob
6
哦,ChatGPT,这个解决方案在我们点击视图中的任何其他插座时都不起作用,你能帮我解决这个问题吗? - TilalHusain
1
如果还有人在看这个问题...我的问题是我的根视图的大部分被UIScrollView覆盖。如果我直接在UIView的边缘点击,键盘将会消失,但如果我在UIScrollView上点击,它就不会消失。 - turkeyhundt

31

我在使用UITableView、Searchbar和键盘时经常采用另一种方法,我认为你可以将其应用到文本字段中。

UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyboard)];
[self.tableView addGestureRecognizer:gestureRecognizer];
gestureRecognizer.cancelsTouchesInView = NO;  // this prevents the gesture recognizers to 'block' touches
[gestureRecognizer release];

然后,这是一个简单的回调函数

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

希望它能帮到你


2
如果您正在使用ViewController,可以将内容添加到[self view]。 - Ben Flynn
不,它不会起作用,表格和滚动视图有自己的交互方式。 - Softlion
这个答案是最好的。而且当我给self.view添加tapGestureRecognizer时,它也适用于scrollView中的一组文本字段。 - Okhan Okbay
这解决了问题。关键在于第三行,它允许其他触摸事件也发生(不阻塞)。它也适用于表视图。在我的情况下,搜索栏位于表视图单元格内部,这不允许出口。在这种情况下,您可以使用 self.view.endEditing(true) 而不是 resignFirstResponder。 - Seop Yoon

7

在我看来:


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];

    // pass touches up to viewController
    [self.nextResponder touchesBegan:touches withEvent:event];
}

在我的视图控制器中:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    UITouch *touch = [[event allTouches] anyObject];
    if (keyboardShown && [touch view] != self.TextView) 
    {
         [self.TextView resignFirstResponder];
    }
    [super touchesBegan:touches withEvent:event];
}

- (void)keyboardWasShown:(NSNotification*)aNotification
{    
        keyboardShown = YES;
}

1
我如何将touchesBegan方法添加到UITableView中,而这个UITableView是我正在工作的UIViewController的self.view的子视图。我是否必须对UITableView进行子类化,例如@interface TouchableTableView:UITableView?或者,有没有更简单的方法可以在不对UITableView进行子类化的情况下完成它? - ma11hew28

3

这是一篇旧文章,但当我在实现这个解决方案时,我发现有一个更简单的方法(也许当时还没有)。

[self.myTextView endEditing:YES];

我正在使用UITableView,所以我把这行代码放在didSelectRowAtIndexPath:方法中。目前为止,一切都很好...


2
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    var touch = event.allTouches()?.anyObject() as UITouch

    if( textfield.isFirstResponder() && touch.view != textfield) {
        textfield.resignFirstResponder()
        NSLog("Resigning first responder since touches began outside")
    }

    super.touchesBegan(touches, withEvent: event)
}

ohhorob的答案对我也有用。这是简单的Swift等效代码。


2
这里有一个更好的解决方案,不依赖于插座,并可以与控制器中的任意数量的UITextField对象一起使用。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    UITouch *touch = [[event allTouches] anyObject];

    if (![[touch view] isKindOfClass:[UITextField class]]) {
        [self.view endEditing:YES];
    }
    [super touchesBegan:touches withEvent:event];
}

不要认为你需要检查UITextField - 即使你不加区分地使用endEditing:,在文本字段之间切换仍然有效,键盘也会保持弹出。 - Nas Banov

2

这里是 Swift 3 代码

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    if let touch = touches.first {
        if textField.isFirstResponder && touch.view != textField {
            textField.resignFirstResponder()
        }
    }
    super.touchesBegan(touches, with: event)
}

1
非常简单。
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self.view endEditing:YES];
}

0

我想要一个可以在任何视图中重新签名而不需要为每个视图指定特定输出的版本。这是我的解决方案,我将其放入了我的标准容器视图之一。

它使用MonoTouch C#编写,但应该很明显如何将其转换为Objective-C。

    private void findAndResignFirstResponder(UIView node=null) {
        if (node == null) { return; }
        if (node.IsFirstResponder) {
            node.ResignFirstResponder();
            return;
        }

        foreach (UIView view in node.Subviews) {
            findAndResignFirstResponder(node:view);
        }
    }

    private bool haveDrag = false;
    public override void TouchesEnded(NSSet touches, UIEvent evt) {
        if (!haveDrag) {
            UITouch touch = (UITouch)evt.AllTouches.AnyObject;
            if (!touch.View.CanBecomeFirstResponder) {
                this.findAndResignFirstResponder(this);
                Console.WriteLine("resign");
            }
        }
        base.TouchesEnded (touches, evt);
    }
    public override void TouchesMoved(NSSet touches, UIEvent evt) {
        haveDrag = true;
        base.TouchesMoved (touches, evt);
    }

    public override void TouchesBegan(NSSet touches, UIEvent evt) {
        haveDrag = false;
        base.TouchesBegan (touches, evt);
    }

0

我的做法是...

  • 在您的ViewController的@interface中添加对TextView的引用:

    @property (weak, nonatomic) IBOutlet UITextView *yourTextView;
    
  • 在您的ViewController的@implementation中重写此方法:

    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    {
        if ([self.yourTextView isFirstResponder]) {
          [self.yourTextView resignFirstResponder];
        }
    }
    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接