UITextView链接可选,无需其他文本可选

我正在尝试获得类似于Facebook使用的设置(如果他们使用UITextView )。 我希望自动检测链接,但我不希望UITextView中的任何其他文本可选。 因此,用户可以单击链接但无法选择任何其他文本。

尽管搜索过,但我还没有找到一个解决方案,因为链接选择工作需要整个文本视图可供选择。

您需要阻止UITextView成为第一响应者。

1.将UITextView子类化到您自己的自定义类( MyTextView )。

2.覆盖canBecomeFirstResponder() 。 这是Swift中的一个例子:

斯威夫特3:

 class MyTextView: UITextView { override func becomeFirstResponder() -> Bool { return false } } 

斯威夫特2:

 class MyTextView: UITextView { override func canBecomeFirstResponder() -> Bool { return false } } 

检测到的任何链接仍将启用。 我用电话号码测试了这个。

你需要gestureRecognizerShouldBegin (_:) UITextView并覆盖gestureRecognizerShouldBegin (_:)方法,如下所示:

 override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if isEditable == false { if let gesture = gestureRecognizer as? UILongPressGestureRecognizer, gesture.minimumPressDuration == 0.5 { return false } } return true } 

这将阻止选择textview,但链接将按预期工作

编辑:事实certificate,当双击并按住时,您仍然可以选择文字。 我发现它发生在两次点击之后(不是具有属性“minimalNumberOfTaps”的UITapGesture,而是一个接一个地进行不同的点击),因此解决方案是在第一步之后跟踪时间(大约0.7秒)完整代码:

 var lastTapTime: TimeInterval = 0 override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if isEditable == false { if let gesture = gestureRecognizer as? UILongPressGestureRecognizer, gesture.minimumPressDuration == 0.5 { return false } } if Date().timeIntervalSince1970 >= lastTapTime + 0.7 { lastTapTime = Date().timeIntervalSince1970 return true } else { return false } } 

这不是最优雅的解决方案,但它似乎工作🤷♂️

所选答案在我的情况下不起作用,我不熟悉比较内部UIGestureRecognizer内部未经证实的值。

我的解决方案是覆盖point(inside:with:) ,当用户没有触及链接文本时允许点击: https : //stackoverflow.com/a/44878203/1153630

如果您的最低部署目标是iOS 11.2或更高版本

您可以通过UITextView并禁止可以选择某些内容的手势来禁用文本选择。

以下解决方案是:

  • 与isEditable兼容
  • 与isScrollEnabled兼容
  • 兼容链接
 /// Class to allow links but no selection. /// Basically, it disables unwanted UIGestureRecognizer from UITextView. /// https://stackoverflow.com/a/49428307/1033581 class UnselectableTappableTextView: UITextView { // required to prevent blue background selection from any situation override var selectedTextRange: UITextRange? { get { return nil } set {} } override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if gestureRecognizer is UIPanGestureRecognizer { // required for compatibility with isScrollEnabled return super.gestureRecognizerShouldBegin(gestureRecognizer) } if let tapGestureRecognizer = gestureRecognizer as? UITapGestureRecognizer, tapGestureRecognizer.numberOfTapsRequired == 1 { // required for compatibility with links return super.gestureRecognizerShouldBegin(gestureRecognizer) } // allowing smallDelayRecognizer for links // https://stackoverflow.com/questions/46143868/xcode-9-uitextview-links-no-longer-clickable if let longPressGestureRecognizer = gestureRecognizer as? UILongPressGestureRecognizer, // comparison value is used to distinguish between 0.12 (smallDelayRecognizer) and 0.5 (textSelectionForce and textLoupe) longPressGestureRecognizer.minimumPressDuration < 0.325 { return super.gestureRecognizerShouldBegin(gestureRecognizer) } // preventing selection from loupe/magnifier (_UITextSelectionForceGesture), multi tap, tap and a half, etc. gestureRecognizer.isEnabled = false return false } } 

如果您的最低部署目标是iOS 11.1或更早版本

原生UITextView链接手势识别器在iOS 11.0-11.1上被破坏,需要一个小的延迟长按而不是点击 : Xcode 9 UITextView链接不再可点击

您可以使用自己的手势识别器正确支持链接,并且可以通过UITextView并禁止可以选择内容或点击某些内容的手势来禁用文本选择。

以下解决方案将禁止选择,并且是:

  • 与isScrollEnabled兼容
  • 兼容链接
  • iOS 11.0和iOS 11.1的解决方法限制,但在点击文本附件时会丢失UI效果
 /// Class to support links and to disallow selection. /// It disables most UIGestureRecognizer from UITextView and adds a UITapGestureRecognizer. /// https://stackoverflow.com/a/49428307/1033581 class UnselectableTappableTextView: UITextView { required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) // Native UITextView links gesture recognizers are broken on iOS 11.0-11.1: // https://stackoverflow.com/questions/46143868/xcode-9-uitextview-links-no-longer-clickable // So we add our own UITapGestureRecognizer. linkGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(textTapped)) linkGestureRecognizer.numberOfTapsRequired = 1 addGestureRecognizer(linkGestureRecognizer) linkGestureRecognizer.isEnabled = true } var linkGestureRecognizer: UITapGestureRecognizer! // required to prevent blue background selection from any situation override var selectedTextRange: UITextRange? { get { return nil } set {} } override func addGestureRecognizer(_ gestureRecognizer: UIGestureRecognizer) { // Prevents drag and drop gestures, // but also prevents a crash with links on iOS 11.0 and 11.1. // https://stackoverflow.com/a/49535011/1033581 gestureRecognizer.isEnabled = false super.addGestureRecognizer(gestureRecognizer) } override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if gestureRecognizer == linkGestureRecognizer { // Supporting links correctly. return super.gestureRecognizerShouldBegin(gestureRecognizer) } if gestureRecognizer is UIPanGestureRecognizer { // Compatibility support with isScrollEnabled. return super.gestureRecognizerShouldBegin(gestureRecognizer) } // Preventing selection gestures and disabling broken links support. gestureRecognizer.isEnabled = false return false } @objc func textTapped(recognizer: UITapGestureRecognizer) { guard recognizer == linkGestureRecognizer else { return } var location = recognizer.location(in: self) location.x -= textContainerInset.left location.y -= textContainerInset.top let characterIndex = layoutManager.characterIndex(for: location, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) let characterRange = NSRange(location: characterIndex, length: 1) if let attachment = attributedText?.attribute(.attachment, at: index, effectiveRange: nil) as? NSTextAttachment { if #available(iOS 10.0, *) { _ = delegate?.textView?(self, shouldInteractWith: attachment, in: characterRange, interaction: .invokeDefaultAction) } else { _ = delegate?.textView?(self, shouldInteractWith: attachment, in: characterRange) } } if let url = attributedText?.attribute(.link, at: index, effectiveRange: nil) as? URL { if #available(iOS 10.0, *) { _ = delegate?.textView?(self, shouldInteractWith: url, in: characterRange, interaction: .invokeDefaultAction) } else { _ = delegate?.textView?(self, shouldInteractWith: url, in: characterRange) } } } } 

这对我有用;

 class LinkDetectingTextView: UITextView { override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if isEditable == false { if let _ = gestureRecognizer as? UITapGestureRecognizer { return false } if let longPressRecognizer = gestureRecognizer as? UILongPressGestureRecognizer, longPressRecognizer.minimumPressDuration == 0.5 { // prevent to select text but allow certain functionality in application return false } } return true } } 

另外,在应用程序中将longPressGestureRecognizer的minimumPressDuration设置为另一个不同于0.5的值。

您可以将UITextView子类UITextView覆盖selectedTextRange的方法,将其设置为nil 。 并且链接仍然是可点击的,但您将无法选择文本的其余部分(即使是链接,但您可以单击它)。

 class CustomTextView: UITextView { override public var selectedTextRange: UITextRange? { get { return nil } set { } }