【问题标题】:UITextView link tap recognition is delayedUITextView 链接点击识别延迟
【发布时间】:2014-04-18 05:52:22
【问题描述】:

在 iOS 7 应用程序中,我有一个带有链接的 UITextView,但点击该链接不会触发。它只会对尴尬的“点击并按住”做出反应。我希望它在用户点击它时立即响应,就像UIWebView 链接点击的工作原理一样。这是我的设置:

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."];
    [text addAttribute:NSLinkAttributeName value:@"myurl://tapped" range:NSMakeRange(6, 16)];

    self.textView.attributedText = text;
    self.textView.editable = NO;
    self.textView.delaysContentTouches = NO;
}

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
{
    if ([[URL scheme] isEqualToString:@"myurl"])
    {
        // Handle tap

        return NO;
    }

    return YES;
}

shouldInteractWithURL 方法的 Apple 文档指出:“如果用户点击或长按 URL 链接,文本视图会调用此方法”。长按可以工作,但点按似乎不起作用。

有谁知道如何让这个立即响应?

【问题讨论】:

  • 链接点击识别延迟在iOS9中仍然发生:((((((

标签: ios objective-c uitextview


【解决方案1】:

如果没有任何迫切需要使用UITextView 的理由,例如显示其他文本,您可以使用UILabel 结合UITapGestureRecognizer 来获得您想要的效果。

否则,您可以使用实际的UIWebView

【讨论】:

  • 我在 UITextView 中做了大量的属性化字符串工作,所以我更喜欢这个而不是构建一个 HTML 字符串。另外,我需要选择特定的文本点,而不是整个文本,所以 UILabel 也出来了。
  • 是的,但是没有触摸高亮。
【解决方案2】:

UITextView 可以选择吗?尝试:

self.textView.selectable = YES;

编辑:

我开始认为,与苹果所说的相反,也许长按是触发它的唯一方法。看看这个link,也许会有所帮助。

【讨论】:

  • 可选择。问题不在于链接不起作用。这是用户必须对其进行延迟触摸。
  • 可能是现在在 iOS 7.1 中修复的错误? link。尝试禁用滚动作为解决方法。
  • 我使用的是 7.1 - 昨天刚刚更新并验证了它在 7.0 和 7.1 中的工作方式。
  • 在设置 URL 之前找到了可能的解决方法:[self.textView setText:nil]self.textView.dataDetectorTypes = UIDataDetectorTypeLink;[self.textView.scrollEnabled:NO];
  • 我开始认为,与苹果所说的相反,也许长按是触发它的唯一方法。检查这个link,也许它会有所帮助。
【解决方案3】:

您是否尝试过设置 textview.delaysContentTouches = NO; ?也许这会有所帮助。

【讨论】:

  • 我有 - 这是我的第一个想法,包含在上面的代码中。我知道 UITextView 继承自 UIScrollView,所以我认为它与此有关。
  • 这也是我的第一直觉。然后,我建议您继续通过touchesBegan 和它的兄弟和attributesAtIndex 工作,并检查NSLinkAttribute 是否存在。
  • 你会怎么做呢?这听起来像是重新实现链接检测...
  • 还有,attributesAtIndex方法在哪里?
  • 是的,看起来确实如此。 attributesAtIndex 在该 UITextView 的 NSAttributedString 上可用。明天晚些时候我可以发布一些代码
【解决方案4】:

正如 nnarayann 提到的,CCHLinkTextView 避免了点击识别延迟的问题。这个库实现了自己的手势识别器,现在在 1.0 版本中可用。

【讨论】:

  • 不错的图书馆!在我的 swift 项目中包含 pod 和调整现有代码只用了 2 分钟。完美运行。此外,该属性采用任何类型的值而不是 NSURL。如果您需要传递诸如索引之类的其他内容,那就太完美了。谢谢!
【解决方案5】:

如果您仍想使用原生UITextView,您可以在您的文本视图中添加一个点击识别器并在点击位置获取字符串属性。找到链接后,可以立即打开。

我为 iOS 7/8 写了一个 Gist 来解决这个问题。它是UITextView 的轻量级扩展,还转发-[UITextViewDelegate textView:shouldInteractWithURL:inRange:] 并公开内部轻击手势识别器。

https://gist.github.com/benjaminbojko/c92ac19fe4db3302bd28

这是一个简单的示例:下面的示例仅适用于 iOS 8。有关 iOS 7 + 8 支持,请参阅上面的要点。

添加您的点击识别器:

// ...

UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedTextView:)];
[myTextView addGestureRecognizer:tapRecognizer];
myTextView.selectable = YES; // otherwise the gesture won't recognize

// ...

并添加您的回调:

- (void)tappedTextView:(UITapGestureRecognizer *)tapGesture {
    if (tapGesture.state != UIGestureRecognizerStateEnded) {
        return;
    }

    UITextView *textView = (UITextView *)tapGesture.view;
    CGPoint tapLocation = [tapGesture locationInView:textView];
    UITextPosition *textPosition = [textView closestPositionToPoint:tapLocation];
    NSDictionary *attributes = [textView textStylingAtPosition:textPosition inDirection:UITextStorageDirectionForward];

    NSURL *url = attributes[NSLinkAttributeName];

    if (url) {
        [[UIApplication sharedApplication] openURL:url];
    }
}

还有快速版本:

点击识别器:

let tapRecognizer = UITapGestureRecognizer(target: self, action: Selector("tappedTextView:"))
myTextView.addGestureRecognizer(tapRecognizer)
myTextView.selectable = true

回调:

func tappedTextView(tapGesture: UIGestureRecognizer) {

        let textView = tapGesture.view as! UITextView
        let tapLocation = tapGesture.locationInView(textView)
        let textPosition = textView.closestPositionToPoint(tapLocation)
        let attr: NSDictionary = textView.textStylingAtPosition(textPosition, inDirection: UITextStorageDirection.Forward)

        if let url: NSURL = attr[NSLinkAttributeName] as? NSURL {
            UIApplication.sharedApplication().openURL(url)
        }

        }

【讨论】:

  • 这正是我所需要的。两个方面说明:1:textView 需要选择才能工作(希望我可以禁用它) 2:attributesNSLinkAttributeName 键产生一个 NSURL,所以上面的代码在 URLWithString 部分失败
  • @BobVork 谢谢!我编辑了我的原始回复以反映您的观点。
  • 我刚在iOS 7上试过这个,看起来textView根本找不到link属性。你在 iOS 7 上遇到过这种情况吗?
  • @BobVork 对,我记得有一些跨版本问题。最终,在 iOS 7 上使用 textStylingAtPosition:inDirection: 获取属性变得有点棘手。我认为它与长度为 0 的 textPosition 有关,因此属性字符串没有实际托管任何链接信息的字符。只是一个想法。最终我写了这个适用于 iOS 7 和 8 的 Gist:gist.github.com/benjaminbojko/c92ac19fe4db3302bd28
  • 斯威夫特 4:@objc func tappedTextView(tapGesture: UIGestureRecognizer) { let textView = tapGesture.view as! UITextView let tapLocation = tapGesture.location(in: textView) let textPosition = textView.closestPosition(to: tapLocation) let attr = textView.textStyling(at: textPosition!, in: .forward)! if let url: URL = attr[NSAttributedStringKey.link.rawValue] as? URL { UIApplication.shared.open(url, options: [:], completionHandler: nil) } }
【解决方案6】:

有时它无法在 iOS 模拟器上运行,或者只有在您点击并按住时才能运行。

您应该在真实设备上进行测试。

别忘了设置selectable

【讨论】:

  • 不知道为什么这个答案被投票赞成,因为我在模拟器和真实设备上看到这个问题的相同行为。
【解决方案7】:

Swift 4解决方案


  • 使用点击手势识别器处理链接
  • 无论isSelectabletrue 还是false,它都有效

let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleLinkTap(_:)))
textView.addGestureRecognizer(tapRecognizer)

@objc func handleLinkTap(_ recognizer: UITapGestureRecognizer) {
    let tapLocation = recognizer.location(in: textView)
    guard
        let textPosition = textView.closestPosition(to: tapLocation),
        let url = textView.textStyling(at: textPosition, in: .forward)?[NSAttributedStringKey.link.rawValue],
        let urlString = (url as? String) ?? (url as? URL)?.absoluteString,
        urlString == "myurl"
    else { return }

    let url = URL(string: urlString)!
    // Do whatever you want with this URL, such as
    UIApplication.shared.openURL(url)
}

【讨论】:

  • 使用这个:let urlString = textView.textStyling(at: textPosition, in: .forward)?[NSAttributedStringKey.link.rawValue] as? URL, urlString == "myurl" else { return } 而不是:let urlString = textView.textStyling(at: textPosition, in: .forward)?[NSAttributedStringKey.link.rawValue] as? String, urlString == "myurl" else { return }
  • 你确定吗?因为(我记得)我试图将结果转换为 URL,我猜它失败了,我会重新检查以确保(尽快)它是否有效
  • 是的,我确定。也从你身边检查
  • 我已经弄清楚了,这取决于链接属性。我已经给出了网址。它也可以带字符串
  • 太棒了!很高兴在不久之后听到你的消息。感谢您与我们联系。
【解决方案8】:

如果你不需要编辑,你可以使用这个:

textView.isEditable = true
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool
{
    return false
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-11
    • 1970-01-01
    • 2021-04-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多