如何在(有时)可编辑的 UITextView 中启用数据检测器

Posted

技术标签:

【中文标题】如何在(有时)可编辑的 UITextView 中启用数据检测器【英文标题】:How to enable Data Detectors in a (sometimes-) editable UITextView 【发布时间】:2010-10-10 15:14:26 【问题描述】:

有谁知道内置的 Notes 应用程序如何设法显示数据检测器,但它的 UITextView 仍可编辑?

我尝试将可编辑默认设置为 NO(因此数据检测器将起作用),然后覆盖 UITextView 的触摸方法并将其设置为 YES,但它最终会滚动到底部,我必须再次点击才能真正开始编辑。

我也尝试过使用轻击手势识别器,同样的事情发生了。我正在尝试查看正在运行的触摸事件并将可编辑设置为“是”,以便 UITextView 将在点击点开始编辑但没有骰子 - 如果我无法让它将触摸事件传递给 UITextView弄乱了它的可编辑属性。

【问题讨论】:

什么是数据检测器?我正在查看我 iphone 上的 Notes 应用程序,但我不知道您指的是什么。 数据检测器是电话号码、地址、URL 等的自动链接。 【参考方案1】:

一个简单的技巧是在 textview 上放置一个透明视图,以拦截触摸事件。当有人按下透明度时,将 textview 设置为可编辑并使其成为第一响应者,以便键盘显示。

然后,当您退出键盘的第一响应者状态(即用户已完成编辑)时,将可编辑标志设置回 NO。

[编辑]

查找字符串的大小,以便确定将触摸/光标映射到的位置。使用这个:

 CGSize *sizeOfString = [YourString sizeWithFont:[UIFont systemFontOfSize:yourTextViewFontSize]
                               constrainedToSize:CGSizeMake(widthOfYourTextView,
                                                            heightOfYourTextView)
                                   lineBreakMode:UILineBreakModeWordWrap];

[编辑 x2] 你试过把两个文本视图放在一起吗?一个在启用数据检测器的情况下不可编辑,另一个可编辑但 alpha 为 0.0。当文本在一个中更改时,将文本复制到另一个。

【讨论】:

谢谢。我在父视图(UIViewController 的视图)上尝试了与 UITapGestureRecognizer 类似的东西。问题是,一旦您将 UITextView 的可编辑属性设置为 YES,它就会使 UITextView 成为第一响应者,但也会将插入点移动到文本的底部 - 我需要它留在用户点击的位置。 你不能也以编程方式设置 selectedRange。即 NSMakeRange(indexOfWhereYouWantCursor, 0);您必须大致弄清楚他们接触的位置,其中有多少个字符。 我也试过了,但我不知道如何将触摸位置映射到选择范围。 谢谢安德鲁。我之前半考虑过双重文本视图,但不以为然。不过它可能会起作用。我会试一试的。也感谢字符串大小技巧 - 看起来它会派上用场。【参考方案2】:

只需创建一个 UITextView 子类,并像这样覆盖func touchesEnded(_:with:)(在 Swift 4 中):

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) 
    super.touchesEnded(touches, with: event)

    // Only triggered when not in editing mode.
    if !isEditable 

        // Turning off editable will make the data detectors available.
        // (I set my data detectors in Interface Builder)
        isEditable = true

        // Retreive the insert point from touch.
        guard let location = touches.first?.location(in: self) else  fatalError() 
        guard let position = closestPosition(to: location) else  fatalError() 
        guard let range: UITextRange = self.textRange(from: position, to: position) else  fatalError() 

        // You will want to make the text view enter editing mode by one tap, not two.
        becomeFirstResponder()

        // Set the insert point.
        selectedTextRange = range
    

由于点击链接或选择字符串时不会调用此函数,因此它适用于数据检测器。

要使数据检测器再次工作,只需将isEditable 设置为false,可能在委托对象中:

func textViewDidEndEditing(_ textView: UITextView) 
    textView.isEditable = false

关键在于从触摸转动插入点。

【讨论】:

除非我遗漏了什么,否则这似乎不起作用。在 ios13、xcode 11 上,不会触发 url 并且 textView 进入编辑模式,无论触摸是否在 url 上。 @FinniusFrog 抱歉耽搁了。我发布了一个我使用的可行解决方案。希望能帮助到你。如果您发现任何可以改进的地方,请告诉我。【参考方案3】:

我可以通过将 yesleon 的答案和answer 结合在一起来实现这一点。我确信它可以改进,但它对我来说是可行的。

下面的代码是我的ListItemTextView 类的一部分,它继承自UITextView

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) 
    super.touchesEnded(touches, with: event)
    
    guard let touchPoint = touches.first?.location(in: self) else  fatalError() 
    
    let glyphIndex = self.layoutManager.glyphIndex(for: touchPoint, in: self.textContainer)
    let glyphRect = self.layoutManager.boundingRect(forGlyphRange: NSRange(location: glyphIndex, length: 1), in: self.textContainer)
    
    if glyphIndex < self.textStorage.length,
       glyphRect.contains(touchPoint),
       self.textStorage.attribute(NSAttributedString.Key.link, at: glyphIndex, effectiveRange: nil) != nil 
        // Then touchEnded on url
        return
     else 
        isEditable = true

        // Retreive the insert point from touch.
        guard let position = closestPosition(to: touchPoint) else  fatalError() 
        guard let range: UITextRange = self.textRange(from: position, to: position) else  fatalError() 

        // You will want to make the text view enter editing mode by one tap, not two.
        becomeFirstResponder()

        // Set the insert point.
        selectedTextRange = range
    

【讨论】:

以上是关于如何在(有时)可编辑的 UITextView 中启用数据检测器的主要内容,如果未能解决你的问题,请参考以下文章

UIAlertController 内的多行可编辑文本 UITextview?

UIAlertController中的多行可编辑文本UITextview?

iOS UIAlertview 与 uitextview 可编辑

选择另一个 UITextView 时,如何阻止 UITextView 退出第一响应者/关闭键盘?

可编辑的 UITextView 可以嵌入到 UIPopoverController 中吗?

如何不为“复制/粘贴”UITextView 选择文本