选择文本时,如何用我自己的视图替换 UIMenuController?

Posted

技术标签:

【中文标题】选择文本时,如何用我自己的视图替换 UIMenuController?【英文标题】:How could I replace UIMenuController with my own view when text is selected? 【发布时间】:2013-01-30 02:16:02 【问题描述】:

当文本被选中时,默认会弹出一个 UIMenuController 并带有剪切/复制/粘贴等功能。

我想用我自己的自定义视图替换它(外观相似,但高度是原来的两倍,这样我就可以拥有两行按钮/自定义视图)。我该怎么做?

我知道没有简单的方法。我期待如果有一个简单的解决方案,它不会很优雅。该代码也不能使用任何私有 API。

我真的,真的宁愿不必实现我自己的文本视图,重新实现文本选择和输入,并重新实现放大视图,这样我就可以编写自己的 UIMenuController 克隆,如果有任何方法可以避免的话。我可以替换 UIMenuController 对应用程序的界面非常重要,所以如果没有其他答案,那么我可能最终会这样做。如果有人能为我节省大量时间并提出另一种更简单的方法,我将不胜感激!

【问题讨论】:

也许会帮助你一点ios-blog.co.uk/tutorials/…和***.com/a/3286756/1702413 @TonyMkenu 谢谢,但是这些教程只是解释了如何将自定义项目添加到 UIMenuController,Apple 提供的 API 很好地支持 - 这不是我想要做的,我想完全替换UIMenuController 和我自己的自定义实现。 【参考方案1】:

在开始之前,您必须了解三件重要的事情:

1) 您必须编写自定义菜单控制器视图,但我猜您有点预料到了。我只知道自定义菜单控制器的commercial 实现,但这应该不会太难。

2)UIResponder 上有一个有用的方法叫做-canPerformAction:withSender:。在UIResponder Class Reference 中了解更多信息。您可以使用该方法来确定您的文本视图是否支持特定的标准操作(在UIResponderStandardEditActions 协议中定义)。 这在决定在自定义菜单控制器中显示哪些项目时很有用。例如,仅当用户的粘贴板包含要粘贴的字符串时,才会显示“粘贴”菜单项。

3)您可以通过收听UIMenuControllerWillShowMenuNotification 通知来检测UIMenuController 何时显示。

既然你都知道了,那么我将开始解决这个问题:

1) 在文本视图为第一响应者时监听UIMenuControllerWillShowMenuNotifications

- (void)textViewDidBeginEditing:(UITextView *)textView 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(menuWillBeShown:) name:UIMenuControllerWillShowMenuNotification object:nil];


- (void)textViewDidEndEditing:(UITextView *)textView 
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIMenuControllerWillShowMenuNotification object:nil];

2) 显示您的自定义菜单控制器而不是默认的UIMenuController

- (void)menuWillBeShown:(NSNotification *)notification 
    CGRect menuFrame = [[UIMenuController sharedMenuController] menuFrame];
    [[UIMenuController sharedMenuController] setMenuVisible:NO animated:NO]; // Don't show the default menu controller

    CustomMenuController *controller = ...;
    controller.menuItems = ...;
    // additional stuff goes here

    [controller setTargetRectWithMenuFrame:menuFrame]; // menuFrame is in screen coordinates, so you might have to convert it to your menu's presenting view/window/whatever

    [controller setMenuVisible:YES animated:YES];

杂项。 1) 您可以使用全屏UIWindow 来显示您的自定义菜单,以便它可以与状态栏重叠。

UIWindow *presentingWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
presentingWindow.windowLevel = UIWindowLevelStatusBar + 1;
presentingWindow.backgroundColor = [UIColor clearColor];

[presentingWindow addSubview:controller];
[presentingWindow makeKeyAndVisible];

杂项。 2) 要确定要显示哪些菜单项,您可以使用提到的-canPerformAction:withSender:

BOOL canPaste = [textView canPerformAction:@selector(paste:) withSender:nil];
BOOL canSelectAll = [textView canPerformAction:@selector(selectAll:) withSender:nil];

杂项。 3) 您必须自己处理关闭菜单,方法是在呈现窗口上使用UITapGestureRecognizer 或类似的东西。

这并不容易,但它是可行的,我希望它对你很有效。祝你好运!

更新: 今天 cocoacontrols.com 上出现了一个新的菜单实现,你可能想看看:https://github.com/questbeat/QBPopupMenu

更新 2: 正如this answer 中所述,您可以使用-caretRectForPosition: 获取文本视图的选定文本的框架。

【讨论】:

谢谢,这是在正确的轨道上!我遇到了几个问题,如果有人知道如何解决这些问题,请说。 1. 显示菜单控制器时的通知有时似乎略有延迟。如果我尝试拦截显示的菜单控制器,就像上面的代码一样并且什么都不显示,偶尔它会在消失之前弹出一秒钟。我还没有想出如何重现这个,但已经注意到了好几次。不是太大的问题,因为它会很快消失,但任何阻止这个小故障的方法都会很棒! 2.自定义控制器箭头的位置。你得到的菜单框架是一个好的开始,但也需要知道箭头的方向。 arrowDirection 属性只是给出了一个没有帮助的“默认”值。实际上不仅需要知道箭头的方向,还需要知道它的位置。箭头应该以选定文本为中心,并且由于菜单控制器的原点受限于屏幕宽度,因此箭头通常不在菜单中间居中。如何找到正确的箭头位置? 1) 我能想到的唯一解决方法是使用自定义手势识别器来重新实现 UIMenuController 的行为。一个用于长按,另一个用于点击选定的文本等。不过,这将是一个相当麻烦的过程。 2) 您必须使用自己的逻辑来确定箭头的位置。通过查看 menuFrame 的中心很容易确定它是在顶部还是在底部。如果 y 值高于,比方说,菜单的高度加上 5 个点,则将其显示在顶部,否则显示在底部。箭头是居中还是向右或向左移动会更复杂。首先,我会使用类似的东西:targetCenter = targetRect.origin.x + targetRect.size.width / 2; arrowXPosition = floor(targetCenter - (targetCenter - menuSize.width / 2)); 1 - 我可能是错的,但如果我重新实现这些,会阻止文本被选中吗? 2 - 我仍然不确定您打算如何确定所选文本的中心在哪里 - 顶部和底部都可以,但它正在找到所选文本的中心,我看不到仅使用给定的菜单框架。【参考方案2】:

我认为这可能会帮助你https://github.com/cxa/UIMenuItem-CXAImageSupport

UIMenuItem 使用 UILabel 来显示它的标题,这意味着我们可以调配 -drawTextInRect: 来支持图像。

UIMenuItem+CXAImageSupport 是一种肮脏的技巧,但在大多数情况下应该是安全的。不包含任何私有 API。

为 UIMenuItem 创建一个类别而不是子类化可以获得更大的灵活性。是的,这个类别也可以应用于很棒的 PSMenuItem!

【讨论】:

谢谢,不过我已经看过了。我需要一个两倍高的菜单控制器,但这无济于事 - 它只是使用一些 hackish 代码将 UIMenuController 中的文本替换为图像。

以上是关于选择文本时,如何用我自己的视图替换 UIMenuController?的主要内容,如果未能解决你的问题,请参考以下文章

如何用批处理替换文本内容?

如何用随机数组元素替换文本?

如何用 dojo 增强网格中的文本替换超链接?

Excel如何用文本填充所有选定的空白单元格

如何用 Javascript 替换和追加

如何用CollapsingToolbar替换CoordinatorLayout中的CardView?