UITextView带有可点击链接但没有文字突出显示

时间:2014-05-15 23:07:37

标签: ios objective-c ios7 uitextview highlight

我有一个显示不可编辑文本的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;
    }
}

有许多类似的线程,但似乎没有解决这个特定的链接启用问题,文字不可突出。

9 个答案:

答案 0 :(得分:24)

我正在处理完全相同的问题,我能做的最好的事情就是通过将以下内容添加到UITextView的委托来立即清除选择:

- (void)textViewDidChangeSelection:(UITextView *)textView {
    if(!NSEqualRanges(textView.selectedRange, NSMakeRange(0, 0))) {
        textView.selectedRange = NSMakeRange(0, 0);
    }
}

请注意检查以防止递归。这几乎解决了这个问题,因为只有选择被禁用 - 链接仍然有用。

另一个切线问题是文本视图仍然会成为第一响应者,您可以通过在设置所选范围后设置所需的第一响应者来解决此问题。

注意:剩下的唯一视觉奇怪之处就是按下并按住放大镜。

答案 1 :(得分:16)

我不确定这是否适用于您的特定情况,但我有一个类似的情况,我需要textview链接可点击但不希望文本选择发生我使用textview显示数据CollectionViewCell。

我只需要覆盖-canBecomeFirstResponder并返回NO

@interface MYTextView : UITextView
@end

@implementation MYTextView

- (BOOL)canBecomeFirstResponder {
    return NO;
}

@end

答案 2 :(得分:10)

正如我在另一篇文章中所写,还有另一种解决方案。

经过几次测试,我找到了解决方案。

如果您希望链接处于活动状态且未启用选择,则需要编辑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)。 我希望它有所帮助! :)

答案 3 :(得分:5)

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
}

答案 4 :(得分:3)

这对我有用。

我无法摆脱放大镜,但这样可以让文字视图保持可选(这样你可以点击链接),但摆脱所有选择相关的UI。仅在iOS 9上测试过。

警告下面的Swift!

首先,子类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
}

如果没有此功能,将禁用选择条和上下文菜单,但仍会在所选文本后面留下彩色背景。此功能摆脱了选择背景。

就像我说的那样,我还没有找到摆脱放大玻璃的方法,但如果他们在链接之外的任何地方做了很长的拍打,一旦放大玻璃消失,就不会留下任何东西。

答案 5 :(得分:2)

这是一个UITextView子类方法,它只能识别落在链接文本上的点。

from openerp.tools.misc import find_in_path
import subprocess

def _get_wkhtmltopdf_bin():
    return find_in_path('wkhtmltopdf')

try:
    process = subprocess.Popen([_get_wkhtmltopdf_bin(), '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except (OSError, IOError):
    _logger.info('You need Wkhtmltopdf to print a pdf version of the reports.')

答案 6 :(得分:2)

这几乎解决了这个问题,因为禁用了文本选择并隐藏了放大镜 - 链接仍然有用。

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)
                }
            }
        }
    }
}

注意:您可以将识别器替换为所需的识别器,而不是删除。

答案 7 :(得分:1)

虽然面对未来可能的实施变化,它确实很脆弱,但KubíkKaš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

答案 8 :(得分:0)

我发现lramirez135Kubík Kašpar的答案几乎解决了这个问题。然而,lramirez135的答案无法解决长按选择的问题,而Kubík Kašpar的答案则取决于iOS版本。

我结合了他们的逻辑,并迅速创建了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