首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >UIMenuController隐藏键盘

UIMenuController隐藏键盘
EN

Stack Overflow用户
提问于 2012-11-28 17:20:37
回答 3查看 3.8K关注 0票数 6

我目前有一个聊天的应用程序。我使用UItextField作为输入框,并使用气泡来显示消息,类似于系统短信。我要在消息气泡(标签)上启用复制粘贴。问题是,当我想要显示UIMenuController时,我需要复制的标签需要成为第一响应者。如果当前显示键盘,当标签变为first responder时,文本字段将失去焦点,因此键盘将自动隐藏。这会导致UI滚动,并且感觉不好。有没有什么方法可以让我在需要显示菜单的时候也能保持键盘显示?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-11-28 20:41:28

您可以尝试将uitextfield子类化并覆盖firstresponder。如果uitextfield是第一个响应者,请检查长按手势处理程序,并覆盖下一个响应者。

票数 2
EN

Stack Overflow用户

发布于 2014-05-25 05:48:09

对于那些仍然在这里寻找答案的人来说,代码(主要思想属于neon1,请参阅链接问题)。

想法如下:如果响应者不知道如何处理给定的操作,它会将其传播给链中的下一个响应者。到目前为止,我们有两个候选人作为急救人员:

  1. Cell
  2. TextField

它们中的每一个都有独立的响应器链(事实上,不,它们确实有共同的祖先,所以它们的链有一些共同之处,但我们不能使用它):

代码语言:javascript
复制
UITextField <- UIView <- ... <- UIWindow <- UIApplication
UITableViewCell <- UIView <- ... <- UIWindow <- UIApplication

因此,我们希望有以下的响应链:

代码语言:javascript
复制
UITextField <- UITableViewCell <- ..... <- UIWindow <- UIApplication

我们需要子类化UITextField (代码取自here):

CustomResponderTextView.h

代码语言:javascript
复制
@interface CustomResponderTextView : UITextView
@property (nonatomic, weak) UIResponder *overrideNextResponder;
@end

CustomResponderTextView.m

代码语言:javascript
复制
@implementation CustomResponderTextView

@synthesize overrideNextResponder;

- (UIResponder *)nextResponder {
    if (overrideNextResponder != nil)
        return overrideNextResponder;
    else
        return [super nextResponder];
}

@end

这段代码非常简单:它返回真正的响应器,以防我们没有设置任何自定义的下一个响应器,否则返回我们的自定义响应器。

现在我们可以在代码中设置新的响应器(我的示例添加了自定义操作):

CustomCell.m

代码语言:javascript
复制
@implementation CustomCell
- (BOOL) canBecomeFirstResponder {
    return YES;
}

- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    return (action == @selector(copyMessage:) || action == @selector(deleteMessage:));
}
@end

- (void) copyMessage:(id)sender {
   // copy logic here
}

- (void) deleteMessage:(id)sender {
   // delete logic here
}

控制器

代码语言:javascript
复制
- (void) viewDidLoad {
    ...
    UIMenuItem *copyItem = [[UIMenuItem alloc] initWithTitle:@"Custom copy" action:@selector(copyMessage:)];
    UIMenuItem *deleteItem = [[UIMenuItem alloc] initWithTitle:@"Custom delete" action:@selector(deleteMessage:)];
    UIMenuController *menu = [UIMenuController sharedMenuController];
    [menu setMenuItems:@[copyItem, deleteItem]];
    ...
}

- (void) longCellTap {
    // cell is UITableViewCell, that has received tap
    if ([self.textField isFirstResponder]) {
        self.messageTextView.overrideNextResponder = cell;
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuDidHide:) name:UIMenuControllerDidHideMenuNotification object:nil];
    } else {
        [cell becomeFirstResponder];
    }
}

- (void)menuDidHide:(NSNotification*)notification {
    self.messageTextView.overrideNextResponder = nil;
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerDidHideMenuNotification object:nil];
}

最后一步是使first responder (在我们的案例中为文本字段)将copyMessage:deleteMessage:操作传播给下一个响应者(在我们的案例中为cell)。正如我们所知道的,iOs发送canPerformAction:withSender:来知道,如果给定响应者可以处理该操作。

我们需要修改CustomResponderTextView.m并添加以下函数:

CustomResponderTextView.m

代码语言:javascript
复制
...
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    if (overrideNextResponder != nil)
        return NO;
    else
        return [super canPerformAction:action withSender:sender];
}
...

如果我们已经设置了自定义的next responder,我们会将所有动作发送给它(如果你需要在textField上做一些动作,你可以修改这个部分),否则我们会询问我们的父类型是否可以处理它。

票数 12
EN

Stack Overflow用户

发布于 2016-10-04 19:59:49

通过Nikita Took的解决方案在Swift中完成。

我有一个聊天屏幕,其中有一个用于文本输入的文本字段和用于消息的标签(它们的显示)。当您轻触邮件标签、菜单(复制/粘贴/...)应该出现,但键盘必须保持打开状态(如果已经打开)。

我对输入文本字段进行了子类化:

代码语言:javascript
复制
import UIKit

class TxtInputField: UITextField {

weak var overrideNextResponder: UIResponder?

override func nextResponder() -> UIResponder? {
  if overrideNextResponder != nil {
    return overrideNextResponder
  } else {
    return super.nextResponder()
  }
}

override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool {
  if overrideNextResponder != nil {
    return false
  } else {
    return super.canPerformAction(action, withSender: sender)
  }
 }
}

然后,在我的自定义消息标签( UILabel的子类,但在本例中它可以是View Controller )中,我添加了以下内容

代码语言:javascript
复制
if recognizer.state == UIGestureRecognizerState.Began { ... 

下面这段代码

代码语言:javascript
复制
if let activeTxtField = getMessageThreadInputSMSField() {
  if activeTxtField.isFirstResponder() {
    activeTxtField.overrideNextResponder = self
  } else {
    self.becomeFirstResponder()
  }
} else {
  self.becomeFirstResponder()
}

当用户在UIMenuController之外点击时

代码语言:javascript
复制
func willHideEditMenu() {
    if let activeTxtField = getMessageThreadInputSMSField() {
      activeTxtField.overrideNextResponder = nil
   }
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIMenuControllerWillHideMenuNotification, object: nil)
  }

您必须获取对activeTxtField对象的引用。我这样做了,迭代了导航堆栈,获得了包含所需文本字段的视图控制器,然后使用它。

以防万一你需要它,这也是该部分的代码片段。

代码语言:javascript
复制
var activeTxtField = CutomTxtInputField()
  for vc in navigationController?.viewControllers {
    if vc is CustomMessageThreadVC {
     let msgVC = vc as! CustomMessageThreadVC         
     activeTxtField = msgVC.textBubble
   }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/13601643

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档