繁体   English   中英

UIMenuController隐藏键盘

[英]UIMenuController hides the keyboard

我目前有一个聊天应用程序。 我将UItextField用于输入框,将气泡用于显示消息,例如系统SMS。 我想在消息气泡(标签)上启用复制粘贴。 问题是,当我想显示UIMenuController时,我需要从中复制的标签需要成为第一响应者。 如果当前显示键盘,则当标签成为第一响应者时,文本字段将失去焦点,因此键盘将自动隐藏。 这会导致UI滚动并且感觉不佳。 无论如何,即使我需要显示菜单,我也可以保持键盘显示吗?

在此处输入图片说明

在此处输入图片说明

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

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

  1. 细胞
  2. 文本域

他们每个人都有独立的响应者链(实际上,不,他们确实有共同的祖先,因此他们的链有共同点,但我们不能使用它):

UITextField <- UIView <- ... <- UIWindow <- UIApplication
UITableViewCell <- UIView <- ... <- UIWindow <- UIApplication

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

UITextField <- UITableViewCell <- ..... <- UIWindow <- UIApplication

我们需要子类化UITextField(代码从此处获取 ):

CustomResponderTextView.h

@interface CustomResponderTextView : UITextView
@property (nonatomic, weak) UIResponder *overrideNextResponder;
@end

CustomResponderTextView.m

@implementation CustomResponderTextView

@synthesize overrideNextResponder;

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

@end

这段代码非常简单:如果我们没有设置任何自定义下一个响应者,它将返回真实的响应者,否则返回我们的自定义响应者。

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

CustomCell.m

@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
}

控制者

- (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];
}

最后一步是使第一响应者(在我们的情况下为文本字段) copyMessage:一个响应者(在我们的情况下为单元格)传播copyMessage:deleteMessage:动作。 众所周知,iOs发送canPerformAction:withSender:来了解给定的响应者是否可以处理该动作。

我们需要修改CustomResponderTextView.m并添加以下功能:

CustomResponderTextView.m

...
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    if (overrideNextResponder != nil)
        return NO;
    else
        return [super canPerformAction:action withSender:sender];
}
...

如果我们设置了自定义的下一个响应者,我们将向其发送所有操作(如果需要对textField进行某些操作,则可以修改此部分),否则我们将询问我们的超类型是否可以处理它。

您可以尝试对uitextfield进行子类化,并覆盖firstresponder。 签入长按手势处理程序,如果uitextfield是第一个响应程序,则覆盖下一个响应程序。

在此处输入图片说明

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

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

我将输入文本字段细分为以下子类:

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)中,该标签具有启动UIMenuController的逻辑,我在

if recognizer.state == UIGestureRecognizerState.Began { ... 

以下块

if let activeTxtField = getMessageThreadInputSMSField() {
  if activeTxtField.isFirstResponder() {
    activeTxtField.overrideNextResponder = self
  } else {
    self.becomeFirstResponder()
  }
} else {
  self.becomeFirstResponder()
}

当用户在UIMenuController之外点击时

func willHideEditMenu() {
    if let activeTxtField = getMessageThreadInputSMSField() {
      activeTxtField.overrideNextResponder = nil
   }
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIMenuControllerWillHideMenuNotification, object: nil)
  }

您必须获取对activeTxtField对象的引用。 我对导航堆栈进行了迭代,获得了包含所需文本字段的View Controller,然后使用了它。

万一您需要它,这里也是该部分的代码段。

var activeTxtField = CutomTxtInputField()
  for vc in navigationController?.viewControllers {
    if vc is CustomMessageThreadVC {
     let msgVC = vc as! CustomMessageThreadVC         
     activeTxtField = msgVC.textBubble
   }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM