带有可点击链接但没有文本突出显示的 UITextView
Posted
技术标签:
【中文标题】带有可点击链接但没有文本突出显示的 UITextView【英文标题】:UITextView with clickable links but no text highlighting 【发布时间】:2014-07-04 14:30:44 【问题描述】:我有一个 UITextView 显示不可编辑的文本。我希望文本自动为用户解析链接、电话号码等,并且让那些可以点击。
不过,我不希望用户能够突出显示文本,因为我想覆盖那些长按和双击交互来做一些不同的事情。
为了在 ios7 中解析链接,需要为 UITextView 打开 Selectable 开关,但 Selectable 也启用了高亮,这是我不想要的。
我尝试覆盖 LongPress 手势以防止突出显示,但这似乎也禁用了对链接的普通点击...
for (UIGestureRecognizer *recognizer in cell.messageTextView.gestureRecognizers)
if ([recognizer isKindOfClass:[UILongPressGestureRecognizer class]])
recognizer.enabled = NO;
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]])
recognizer.enabled = YES;
那里有很多类似的主题,但似乎没有一个可以解决链接启用、文本无法突出显示的特定问题。
【问题讨论】:
【参考方案1】:我正在解决完全相同的问题,我能做的最好的事情是通过将以下内容添加到 UITextView 的委托来立即清除选择:
- (void)textViewDidChangeSelection:(UITextView *)textView
if(!NSEqualRanges(textView.selectedRange, NSMakeRange(0, 0)))
textView.selectedRange = NSMakeRange(0, 0);
注意检查以防止递归。这几乎解决了这个问题,因为只有选择被禁用——链接仍然有效。
另一个无关紧要的问题是文本视图仍将成为第一响应者,您可以通过在设置所选范围后设置所需的第一响应者来解决此问题。
注意:唯一剩下的视觉怪异是按住会打开放大镜。
【讨论】:
有没有没有拿放大镜的? @Neelesh 将 textView.selectedTextRange 设置为 nil,但会存在递归。它很少见,我无法理解,但测试团队做到了。但我不确定您是否需要检查 selectedTextRange 是否为零,或者您可以在 selectedRange 上检查 Range(0,0)。【参考方案2】:我不确定这是否适用于您的特定情况,但我有一个类似的情况,我需要 textview 链接是可点击的,但不希望发生文本选择,我正在使用 textview 在集合视图单元。
我只需要覆盖 -canBecomeFirstResponder
并返回 NO
。
@interface MYTextView : UITextView
@end
@implementation MYTextView
- (BOOL)canBecomeFirstResponder
return NO;
@end
【讨论】:
这似乎运作良好。比之后尝试清除选择要好得多。 也没有放大镜故障!这是一个更好的答案。 适用于 iOS 10.3 不适用于我,iOS 10.3.1 模拟器(注意,我的文本视图不可编辑)。该方法被调用,但文本选择仍然发生。【参考方案3】:正如我在另一篇文章中所写,经过几次测试,我找到了另一个解决方案。
如果您希望链接处于活动状态并且不想启用选择,则需要编辑gestureRecognizers。
例如 - 有 3 个 LongPressGestureRecognizers。一个用于单击链接(minimumPressDuration = 0.12),第二个用于放大可编辑模式(minimumPressDuration = 0.5),第三个用于选择(minimumPressDuration = 0.8)。此解决方案删除了用于选择的 LongPressGestureRecognizer 和用于缩放编辑模式的第二个。
NSArray *textViewGestureRecognizers = self.captionTextView.gestureRecognizers;
NSMutableArray *mutableArrayOfGestureRecognizers = [[NSMutableArray alloc] init];
for (UIGestureRecognizer *gestureRecognizer in textViewGestureRecognizers)
if (![gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]])
[mutableArrayOfGestureRecognizers addObject:gestureRecognizer];
else
UILongPressGestureRecognizer *longPressGestureRecognizer = (UILongPressGestureRecognizer *)gestureRecognizer;
if (longPressGestureRecognizer.minimumPressDuration < 0.3)
[mutableArrayOfGestureRecognizers addObject:gestureRecognizer];
self.captionTextView.gestureRecognizers = mutableArrayOfGestureRecognizers;
在 iOS 9 上测试,但它应该适用于所有版本(iOS 7、8、9)。 我希望它有帮助! :)
【讨论】:
可能不是最终解决方案,因为它可能会在未来的版本中中断,但目前唯一真正有效的解决方案。太好了!【参考方案4】:这对我有用。
我无法摆脱放大镜,但这将允许您保持文本视图可选择(以便您可以点击链接),但摆脱所有与选择相关的 UI。仅在 iOS 9 上测试。
注意下面的斯威夫特!
首先,继承UITextView
并包含这个函数:
override func canPerformAction(action: Selector, withSender sender: AnyObject?) -> Bool
return false
这将禁用复制等菜单。然后我包含一个设置方法,我从 init 调用它,我在其中执行一堆与设置相关的任务。 (我只使用故事板中的这些文本视图,因此解码器初始化):
required init?(coder aDecoder: NSCoder)
super.init(coder: aDecoder)
setup()
private func setup()
selectable = true
editable = false
tintColor = UIColor.clearColor()
Selectable = true 保持链接可点击,editable = false 因为链接在可编辑文本视图中不可点击。指定一个明确的tintColor
会隐藏出现在所选内容开头和结尾的蓝条。
最后,在使用子类文本视图的控制器中,确保包含UITextViewDelegate
协议,设置委托textView.delegate = self
,并实现此委托功能:
func textViewDidChangeSelection(textView: UITextView)
var range = NSRange()
range.location = 0
range.length = 0
textView.selectedRange = range
如果没有此功能,选择栏和上下文菜单将被禁用,但您选择的文本后面仍会留下彩色背景。这个函数摆脱了选择背景。
就像我说的,我还没有找到摆脱放大镜的方法,但是如果他们在链接之外的任何地方长按,一旦放大镜消失,什么都不会留下。
【讨论】:
嗨@RyJ,你现在找到摆脱放大镜的方法了吗/谢谢 否,但我们已经改用 TTTAttributedLabel,github.com/TTTAttributedLabel/TTTAttributedLabel。没有放大镜,没有可选择的文本,并且摆脱了触发文本视图链接所需的长按。如果您需要显示带有内联链接的文本,则解决方案要好得多。【参考方案5】:尽管面对未来可能的实施变化,它确实很脆弱,但 Kubík Kašpar 的方法是唯一对我有用的方法。
但是 (a) 如果您将 UITextView
子类化并且 (b) 如果您想要允许的唯一交互是链接点击,那么这可以变得更简单,您可以立即识别点击:
@interface GMTextView : UITextView
@end
@implementation GMTextView
- (void)addGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
// discard all recognizers but the one that activates links, by just not calling super
// (in iOS 9.2.3 a short press for links is 0.12s, long press for selection is 0.75s)
if ([gestureRecognizer isMemberOfClass:UILongPressGestureRecognizer.class] &&
((UILongPressGestureRecognizer*)gestureRecognizer).minimumPressDuration < 0.25)
((UILongPressGestureRecognizer*)gestureRecognizer).minimumPressDuration = 0.0;
[super addGestureRecognizer:gestureRecognizer];
@end
【讨论】:
【参考方案6】:这是一个 UITextView 子类方法,它只识别链接文本上的点。
class LinkTextView: UITextView
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool
let tapLocation = point.applying(CGAffineTransform(translationX: -textContainerInset.left, y: -textContainerInset.top))
let characterAtIndex = layoutManager.characterIndex(for: tapLocation, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
let linkAttributeAtIndex = textStorage.attribute(.link, at: characterAtIndex, effectiveRange: nil)
// Returns true for points located on linked text
return linkAttributeAtIndex != nil
override func becomeFirstResponder() -> Bool
// Returning false disables double-tap selection of link text
return false
【讨论】:
这会在您点击文本时突出显示文本。 有objc版本吗?【参考方案7】:这几乎解决了文本选择被禁用并隐藏放大镜的问题——链接仍然有效。
func textViewDidChangeSelection(_ textView: UITextView)
if let gestureRecognizers = textView.gestureRecognizers
for recognizer in gestureRecognizers
if recognizer is UILongPressGestureRecognizer
if let index = textView.gestureRecognizers?.index(of: recognizer)
textView.gestureRecognizers?.remove(at: index)
注意:您可以将识别器替换为所需的识别器,而不是删除。
【讨论】:
【参考方案8】:Swift 4、Xcode 9.2
下面是一些不同的方法,
class TextView: UITextView
//MARK: Properties
open var didTouchedLink:((URL,NSRange,CGPoint) -> Void)?
override init(frame: CGRect, textContainer: NSTextContainer?)
super.init(frame: frame, textContainer: textContainer)
required init?(coder aDecoder: NSCoder)
super.init(coder: aDecoder)
override func draw(_ rect: CGRect)
super.draw(rect)
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
let touch = Array(touches)[0]
if let view = touch.view
let point = touch.location(in: view)
self.tapped(on: point)
extension TextView
fileprivate func tapped(on point:CGPoint)
var location: CGPoint = point
location.x -= self.textContainerInset.left
location.y -= self.textContainerInset.top
let charIndex = layoutManager.characterIndex(for: location, in: self.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
guard charIndex < self.textStorage.length else
return
var range = NSRange(location: 0, length: 0)
if let attributedText = self.attributedText
if let link = attributedText.attribute(NSAttributedStringKey.link, at: charIndex, effectiveRange: &range) as? URL
print("\n\t##-->You just tapped on '\(link)' withRange = \(NSStringFromRange(range))\n")
self.didTouchedLink?(link, range, location)
如何使用,
let textView = TextView()//Init your textview and assign attributedString and other properties you want.
textView.didTouchedLink = (url,tapRange,point) in
//here goes your other logic for successfull URL location
【讨论】:
【参考方案9】:我发现lramirez135 和Kubík Kašpar 的答案几乎解决了这个问题。但是lramirez135 的答案无法处理长按选择,而Kubík Kašpar 的答案取决于iOS 版本。
我结合了他们的逻辑并在 swift 中创建了 UITextView 的这个子类,这对我有用。
class CustomUITextView: UITextView
override var canBecomeFirstResponder: Bool
return false
init()
super.init(frame: .zero, textContainer: nil)
guard let textViewGestureRecognizers = self.gestureRecognizers else return
for textViewGestureRecognizer in textViewGestureRecognizers
if textViewGestureRecognizer.isKind(of: UILongPressGestureRecognizer.self)
textViewGestureRecognizer.isEnabled = false
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
Objective-c 版本:
@implementation CustomTextView
- (BOOL)canBecomeFirstResponder
return NO;
- (instancetype)init
self = [super init];
if (self)
self.backgroundColor = UIColor.whiteColor;
self.textContainerInset = UIEdgeInsetsZero;
self.textContainer.lineFragmentPadding = 0;
self.editable = NO;
self.scrollEnabled = NO;
self.linkTextAttributes = @ NSForegroundColorAttributeName: UIColor.orangeColor ;
NSArray<UIGestureRecognizer *> *textViewGestureRecognizers = (NSArray<UIGestureRecognizer *> *)self.gestureRecognizers;
for (UIGestureRecognizer *textViewGestureRecognizer in textViewGestureRecognizers)
if ([textViewGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]])
[textViewGestureRecognizer setEnabled:NO];
return self;
@end
【讨论】:
以上是关于带有可点击链接但没有文本突出显示的 UITextView的主要内容,如果未能解决你的问题,请参考以下文章
Android - ListView 没有收到带有可点击链接的文本视图的 OnItemClick