UITextView中的超链接

24

我正在尝试创建一个带有超链接的UITextView,以便用户点击链接时会跳转到safari中打开网页。我已经阅读了关于 textview 的链接探测器示例,但这些示例总是在文本中存在实际的 URL 时才能起作用(例如:www.google.com)。我想让它成为常规文本,并在点击时打开相关的 URL(例如:Google 是文本,当点击时,打开一个 url www.google.com)。 我如何在 iOS7/8 上实现这个功能?


使用 NSAttributedStringNSLinkAttributeName - Larme
6个回答

33

使用 NSAttributedString

NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:@"Google" 
                                                                       attributes:@{ NSLinkAttributeName: [NSURL URLWithString:@"http://www.google.com"] }];
self.textView.attributedText = attributedString;

当然,您可以将文本的一部分设置为链接。请在此处了解有关NSAttributedString的更多信息。

如果您想在打开链接之前进行更多控制和操作,则可以将代理设置为UITextView。

- (void)viewDidLoad {
    ...
    self.textView.delegate = self; // self must conform to UITextViewDelegate protocol
}

...

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {
    // Do whatever you want here
    NSLog(@"%@", URL); // URL is an instance of NSURL of the tapped link
    return YES; // Return NO if you don't want iOS to open the link
}

快速跟进一下:我如何获得当点击时返回被点击网址的能力,以便我可以发送网络请求?例如,点击Google将会激活某个选择器,该选择器将传递NSURL www.google.com)。 - John Baum
self(或其他你想要的名称)设置为UITextView的代理。然后实现textView:shouldInteractWithURL:inRange:方法。我会更新我的答案以反映这个问题。 - yusuke024
如果我想自己处理链接打开或者想对链接进行其他操作(例如将其传递给另一个对象以启动网络请求),该怎么办呢?理想情况下,我希望能够获取刚刚点击的文本的URL。我该如何做到这一点呢? - John Baum
1
- textView:shouldInteractWithURL:inRange: 方法中,您可以获取被点击的 URL(一个 NSURL 实例)。您可以对其进行任何操作。只需在该委托方法中返回 NO,即可防止 iOS 打开链接。 - yusuke024
以防万一,如果 textView:shouldInteractWithURL:inRange:deprecated 并被替换为 textView:shouldInteractWithURL:inRange:interaction: - Bruno Bieri

19

Swift 3, iOS10 , Xcode 9

@sikhapol的答案非常好,如果你知道要解析的词语,比如“单词词典”之类的。

关键在于 UITextView 中显示的字符串本身

我的解决方案基于文本渲染,如果你让 UITextView 渲染 HTML 标签,就可以使用 href 标签了

下面是一些代码参考:

首先,你需要从界面生成器或代码中配置 UITextView

  1. Selectable
  2. Data detectors

注意:不要让 UITextView 可编辑

界面生成器

enter image description here

程序代码

         let htmlData = NSString(string: "go to <a href=\"http://www.google.com\">google</a> and search for it").data(using: String.Encoding.unicode.rawValue)


        let attributedString = try! NSAttributedString(data: htmlData!, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
        yourUIViewView.isSelectable = true
        yourUIViewView.dataDetectorTypes = .link
        yourUIViewView.attributedText = attributedString
        yourUIViewView.delegate = self

针对UITextViewDelegate

    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
    
       // check for the url string for performing your own custom actions here
      let urlString = URL.absoluteString
    
       // Return NO if you don't want iOS to open the link
        return true
     }

3
这里有一种更现代的方法来实现这个功能:let attributedString = try? NSAttributedString(data: Data("一些<em>酷炫</em>的文本".utf8), options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) - Nikolay Suvandzhiev

13

我编写并使用的一个小巧实用的扩展(Swift 4.2,测试过iOS 12.1)

extension NSAttributedString {
    func replace(placeholder: String, with hyperlink: String, url: String) -> NSAttributedString {
        let mutableAttr = NSMutableAttributedString(attributedString: self)

        let hyperlinkAttr = NSAttributedString(string: hyperlink, attributes: [NSAttributedString.Key.link: URL(string: url)!])

        let placeholderRange = (self.string as NSString).range(of: placeholder)

        mutableAttr.replaceCharacters(in: placeholderRange, with: hyperlinkAttr)
        return mutableAttr
    }
}

用法:

//Set through code or through interface builder
footerText.isSelectable = true
footerText.dataDetectorTypes = .link

//Keeps the original formatting from xib or storyboard
footerText.text = "By continuing, you are indicating that you accept our @Terms@ and @Privacy@."
footerText.attributedText = footerText.attributedText?
        .replace(placeholder: "@Terms@", with: "Terms and Conditions", url: AppUrl.terms)
        .replace(placeholder: "@Privacy@", with: "Privacy Policy", url: AppUrl.privacy)

如果您将textView的字体传递给扩展函数并将其添加到hyperlinkAttr中,则所有文本都将具有相同的字体和大小。 - Ibrahim
1
运行良好,但我必须添加:textView.isEditable = false - ingconti
我想在TextView上禁用文本选择,因为在点击字符串的其他部分时会打开选择控制器,而我不希望这样。如果我将isSelectable设置为false,则链接将无法点击。 - Satish Mavani

1
这个代码示例在同一个标签中有两个不同的链接,URL颜色已设置为避免默认的蓝色。
UITextView * textTerm = [UITextView new];
NSMutableAttributedString *attrRight = [[NSMutableAttributedString alloc] initWithString:@"Terms of Service"
                                                                       attributes:@{ NSLinkAttributeName: [NSURL URLWithString:@"http://www.google.com"] }];
NSMutableAttributedString *attrLeft = [[NSMutableAttributedString alloc] initWithString:@"Privacy Policy"
                                                                       attributes:@{ NSLinkAttributeName: [NSURL URLWithString:@"http://www.google.com"] }];
[attrRight appendAttributedString:attrLeft];
textTerm.attributedText = attrRight;
textTerm.editable = NO;
textTerm.dataDetectorTypes = UIDataDetectorTypeAll;
textTerm.linkTextAttributes = [UIColor whiteColor];
textTerm.backgroundColor = [UIColor clearColor];

0
如果您想在UITextView中使用活动子字符串,那么您可以使用我的扩展TextView...它简短而简单。您可以根据需要进行编辑。
如何使用(范围=子字符串位置):
[self.textView addTapActionWithRange:range withActionBlock:^{      // anything you want to do - show something}];

结果: 在此输入图片描述

源代码:https://github.com/marekmand/ActiveSubstringTextView


0

由于编辑队列已满,我将在此处发布我的答案版本。它基于Amr Angry的答案。

我正在使用DispatchQueue.main.async { ... },因为如果NSAttributedString在后台线程上运行,应用程序将崩溃。

guard let htmlData = NSString(string: "go to <a href=\"http://www.google.com\">google</a> and search for it").data(using: String.Encoding.unicode.rawValue) else { return }
    
DispatchQueue.main.async {
    do {

    let attributedString = try NSAttributedString(data: htmlData, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
    yourUIViewView.isSelectable = true
    yourUIViewView.dataDetectorTypes = .link
    yourUIViewView.attributedText = attributedString
    yourUIViewView.delegate = self

    } catch {
        print("Cannot setup link with \(htmlData)!")
    }
}

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {

    // Return NO if you don't want iOS to open the link
    return true
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接