【问题标题】:UITextView with hyperlink text带有超链接文本的 UITextView
【发布时间】:2017-01-07 09:07:54
【问题描述】:

使用不可编辑的 UITextView,我想在 iOS9+ 中嵌入这样的文本:

只需click here 注册

我可以创建一个函数并操作文本,但有没有更简单的方法?

我发现我可以使用 NSTextCheckingTypeLink,因此在 Interface Builder 中无需“单击此处”部分即可轻松获得可点击的文本:

只需http://example.com 注册

如果相关,我正在使用 Xcode 8 和 Swift 3。

【问题讨论】:

    标签: swift interface-builder ios9 xcode8


    【解决方案1】:

    设置isEditable = false 或者当用户点击它时文本视图将进入文本编辑模式。

    Swift 4 及更高版本

    let attributedString = NSMutableAttributedString(string: "Just click here to register")
    let url = URL(string: "https://www.apple.com")!
    
    // Set the 'click here' substring to be the link
    attributedString.setAttributes([.link: url], range: NSMakeRange(5, 10))
    
    self.textView.attributedText = attributedString
    self.textView.isUserInteractionEnabled = true
    self.textView.isEditable = false
    
    // Set how links should appear: blue and underlined
    self.textView.linkTextAttributes = [
        .foregroundColor: UIColor.blue,
        .underlineStyle: NSUnderlineStyle.single.rawValue
    ]
    

    【讨论】:

    • UITextItemInteraction 是 iOS10+ 但我需要 iOS9+。我会看看我能不能解决这个问题并在一秒钟内投票。
    • 在 swift 4 中,属性已从 NSLinkAttributedName 重命名为 .link,NSForegroundColorAttributeName 重命名为 .foregroundColor
    • 如果您还没有阅读过这个问题,请注意上面示例中的 NSMakeRange(5, 10) 可以解释为从 5 开始的范围 -> 第一个字符 ('c') 的索引到并排除 10 -> 最后一个字符 ('k') 的索引,但这样你可能会得到一个越界错误。 NSMakeRange 实际上需要 5 -> 第一个字符('c')的索引和 10 -> 范围的长度。
    • 我也需要以下答案才能使链接可点击。 stackoverflow.com/a/14387132/5845039
    【解决方案2】:

    如果您想使用多个超链接,您可以为 Swift 5

    使用此替代方法
    extension UITextView {
    
      func addHyperLinksToText(originalText: String, hyperLinks: [String: String]) {
        let style = NSMutableParagraphStyle()
        style.alignment = .left
        let attributedOriginalText = NSMutableAttributedString(string: originalText)
        for (hyperLink, urlString) in hyperLinks {
            let linkRange = attributedOriginalText.mutableString.range(of: hyperLink)
            let fullRange = NSRange(location: 0, length: attributedOriginalText.length)
            attributedOriginalText.addAttribute(NSAttributedString.Key.link, value: urlString, range: linkRange)
            attributedOriginalText.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: fullRange)
            attributedOriginalText.addAttribute(NSAttributedString.Key.font, value: YourFont, range: fullRange)
        }
        
        self.linkTextAttributes = [
            NSAttributedString.Key.foregroundColor: YourColor,
            NSAttributedString.Key.underlineStyle: NSUnderlineStyle.single.rawValue,
        ]
        self.attributedText = attributedOriginalText
      }
    }
    

    用法:

    yourTextView.addHyperLinksToText(originalText: "Testing hyperlinks here and there", hyperLinks: ["here": "someUrl1", "there": "someUrl2"])
    

    【讨论】:

    • 它有效!谢谢。这对多个链接非常有用! :)
    【解决方案3】:

    使用扩展的 Swift 3 的相同解决方案:

    A.添加扩展 -

    extension UITextView {
        func hyperLink(originalText: String, hyperLink: String, urlString: String) {
            let style = NSMutableParagraphStyle()
            style.alignment = .center
            let attributedOriginalText = NSMutableAttributedString(string: originalText)
            let linkRange = attributedOriginalText.mutableString.range(of: hyperLink)
            let fullRange = NSMakeRange(0, attributedOriginalText.length)
            attributedOriginalText.addAttribute(NSLinkAttributeName, value: urlString, range: linkRange)
            attributedOriginalText.addAttribute(NSParagraphStyleAttributeName, value: style, range: fullRange)
            attributedOriginalText.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: 10), range: fullRange)
            self.linkTextAttributes = [
                NSForegroundColorAttributeName: UIConfig.primaryColour,
                NSUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue,
            ]
            self.attributedText = attributedOriginalText
        }
    }
    

    B.添加链接地址-let linkUrl = "https://www.my_website.com"

    C.像这样在你的 ViewController 中实现UITextViewDelegate -

     class MyViewController: UIViewController, UITextViewDelegate { 
     }
    

    D.添加委托方法来处理点击事件 -

    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
        if (URL.absoluteString == linkUrl) {
            UIApplication.shared.openURL(URL)
        }
        return false
        }
    }
    

    E.最后,在属性检查器下为您的UITextView 确保的事情-

    1. 行为 - Editable 已关闭,Selectable 已打开。
    2. 数据检测器 - 链接已打开。

    用法-

    textView.hyperLink(originalText: "To find out more please visit our website", hyperLink: "website", urlString: linkUrl)
    

    干杯,编码愉快!

    【讨论】:

    • 别忘了加textView.delegate = self
    【解决方案4】:

    斯威夫特 5 这是基于 Tejas 的回答,因为这两个类中的一些项目已被弃用。

    extension UITextView {
    
    
    func hyperLink(originalText: String, hyperLink: String, urlString: String) {
    
        let style = NSMutableParagraphStyle()
        style.alignment = .left
    
        let attributedOriginalText = NSMutableAttributedString(string: originalText)
        let linkRange = attributedOriginalText.mutableString.range(of: hyperLink)
        let fullRange = NSMakeRange(0, attributedOriginalText.length)
        attributedOriginalText.addAttribute(NSAttributedString.Key.link, value: urlString, range: linkRange)
        attributedOriginalText.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: fullRange)
        attributedOriginalText.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.blue, range: fullRange)
        attributedOriginalText.addAttribute(NSAttributedString.Key.font, value: UIFont.systemFont(ofSize: 10), range: fullRange)
    
        self.linkTextAttributes = [
            kCTForegroundColorAttributeName: UIColor.blue,
            kCTUnderlineStyleAttributeName: NSUnderlineStyle.single.rawValue,
            ] as [NSAttributedString.Key : Any]
    
        self.attributedText = attributedOriginalText
    }
    

    不要忘记将 UITextViewDelegate 添加到您的视图控制器并设置您的 let linkUrl = "https://example.com"

    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
        if (URL.absoluteString == linkUrl) {
            UIApplication.shared.open(URL) { (Bool) in
    
            }
        }
        return false
    }
    

    用法保持不变:

    textView.hyperLink(originalText: "To find out more please visit our website", hyperLink: "website", urlString: linkUrl)
    

    【讨论】:

    • 您必须从情节提要“属性检查器”设置“TextView 的数据检测器”->“链接”属性。那么只有这段代码可以工作。
    【解决方案5】:

    Swift 4 代码。 可能是我是唯一需要在一封邮件中设置多个链接并为文字着色的人。我创建了一个 AttribTextHolder 类来累积有关此持有人内文本的所有信息,并轻松地在对象之间传递它以将文本设置为 UITextView 在控制器深处的某个地方。

    class AttribTextHolder {
    
            enum AttrType {
                case link
                case color
            }
    
            let originalText: String
            var attributes: [(text: String, type: AttrType, value: Any)]
    
    
            init(text: String, attrs: [(text: String, type: AttrType, value: Any)] = [])
            {
                originalText = text
                attributes = attrs
            }
    
            func addAttr(_ attr: (text: String, type: AttrType, value: Any)) -> AttribTextHolder {
                attributes.append(attr)
                return self
            }
    
            func setTo(textView: UITextView)
            {
                let style = NSMutableParagraphStyle()
                style.alignment = .left
    
                let attributedOriginalText = NSMutableAttributedString(string: originalText)
    
                for item in attributes {
                    let arange = attributedOriginalText.mutableString.range(of: item.text)
                    switch item.type {
                    case .link:
                        attributedOriginalText.addAttribute(NSAttributedString.Key.link, value: item.value, range: arange)
                    case .color:
                        var color = UIColor.black
                        if let c = item.value as? UIColor { color = c }
                        else if let s = item.value as? String { color = s.color() }
                        attributedOriginalText.addAttribute(NSAttributedString.Key.foregroundColor, value: color, range: arange)
                    default:
                        break
                    }
                }
    
                let fullRange = NSMakeRange(0, attributedOriginalText.length)
                attributedOriginalText.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: fullRange)
    
                textView.linkTextAttributes = [
                    kCTForegroundColorAttributeName: UIColor.blue,
                    kCTUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue,
                ] as [String : Any]
    
                textView.attributedText = attributedOriginalText
            }
     }
    

    像这样使用它:

     let txt = AttribTextHolder(text: "To find out more visit our website or email us your questions")
                .addAttr((text: "our website", type: .link, "http://example.com"))
                .addAttr((text: "our website", type: .color, "#33BB22"))
                .addAttr((text: "email us", type: .link, "mailto:us@example.com"))
                .addAttr((text: "email us", type: .color, UIColor.red))
     ....
     ....
     txt.setTo(textView: myUITextView)
    

    同样在这段代码中,我使用简单的字符串扩展将字符串十六进制值转换为 UIColor 对象

    extension String {
    /// Converts string color (ex: #23FF33) into UIColor
    func color() -> UIColor {
        let hex = self.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
        var int = UInt32()
        Scanner(string: hex).scanHexInt32(&int)
        let a, r, g, b: UInt32
        switch hex.characters.count {
        case 3: // RGB (12-bit)
            (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
        case 6: // RGB (24-bit)
            (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
        case 8: // ARGB (32-bit)
            (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
        default:
            (a, r, g, b) = (255, 0, 0, 0)
        }
        return UIColor(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
      }
    }
    

    【讨论】:

    • 这是很棒的东西。考虑将其打包到库中!
    【解决方案6】:

    使用 Swift >= 4:

    let descriptionText = NSMutableAttributedString(string:"To learn more, check out our ", attributes: [:])
    
    let linkText = NSMutableAttributedString(string: "Privacy Policy and Terms of Use", attributes: [NSAttributedString.Key.link: URL(string: example.com)!])
    
    descriptionText.append(linkText)
    

    【讨论】:

      【解决方案7】:

      Swift 4 使用扩展的相同解决方案:

      extension UITextView {
      
      
          func hyperLink(originalText: String, hyperLink: String, urlString: String) {
      
                  let style = NSMutableParagraphStyle()
                  style.alignment = .left
      
                  let attributedOriginalText = NSMutableAttributedString(string: originalText)
                  let linkRange = attributedOriginalText.mutableString.range(of: hyperLink)
                  let fullRange = NSMakeRange(0, attributedOriginalText.length)
                  attributedOriginalText.addAttribute(NSAttributedStringKey.link, value: urlString, range: linkRange)
                  attributedOriginalText.addAttribute(NSAttributedStringKey.paragraphStyle, value: style, range: fullRange)
                  attributedOriginalText.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.blue, range: fullRange)
                  attributedOriginalText.addAttribute(NSAttributedStringKey.font, value: UIFont.systemFont(ofSize: 10), range: fullRange)
      
                  self.linkTextAttributes = [
                     kCTForegroundColorAttributeName: UIColor.blue,
                     kCTUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue,
                  ] as [String : Any]
      
      
                  self.attributedText = attributedOriginalText
              }
      
      }
      

      【讨论】:

        【解决方案8】:

        通过 UITextView 实现超链接的更安全的解决方案

        var termsConditionsTextView: UITextView = {
        let view = UITextView()
         view.backgroundColor = .clear
         view.textAlignment = .left
         
         let firstTitleString = "By registering for THIS_APP I agree with the "
         let secondTitleString = "Terms & Conditions"
         let finishTitleString = firstTitleString + secondTitleString
         let attributedString = NSMutableAttributedString(string: finishTitleString)
         attributedString.addAttribute(.link, value: "https://stackoverflow.com", range: NSRange(location: firstTitleString.count, length: secondTitleString.count))
         
         view.attributedText = attributedString
         view.textContainerInset = .zero
         view.linkTextAttributes = [
             .foregroundColor: UIColor.blue,
             .underlineStyle: NSUnderlineStyle.single.isEmpty
         ]
         
         view.font = view.font = UIFont(name: "YOUR_FONT_NAME", size: 16)
         view.textColor = UIColor.black
         
         return view }()
        

        【讨论】:

          【解决方案9】:

          SWIFT 5 和多个链接

          import UIKit
          
          public extension UITextView {
              
              func hyperLink(originalText: String, linkTextsAndTypes: [String: String]) {
                  
                  let style = NSMutableParagraphStyle()
                  style.alignment = .left
                  
                  let attributedOriginalText = NSMutableAttributedString(string: originalText)
                  
                  for linkTextAndType in linkTextsAndTypes {
                      let linkRange = attributedOriginalText.mutableString.range(of: linkTextAndType.key)
                      let fullRange = NSRange(location: 0, length: attributedOriginalText.length)
                      attributedOriginalText.addAttribute(NSAttributedString.Key.link, value: linkTextAndType.value, range: linkRange)
                      attributedOriginalText.addAttribute(NSAttributedString.Key.paragraphStyle, value: style, range: fullRange)
                      attributedOriginalText.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.blue, range: fullRange)
                      attributedOriginalText.addAttribute(NSAttributedString.Key.font, value: UIFont.systemFont(ofSize: 10), range: fullRange)
                  }
                  
                  self.linkTextAttributes = [
                      kCTForegroundColorAttributeName: UIColor.blue,
                      kCTUnderlineStyleAttributeName: NSUnderlineStyle.single.rawValue
                  ] as [NSAttributedString.Key: Any]
                  
                  self.attributedText = attributedOriginalText
              }
          }
          

          以及在您的 viewController 中的用法:

          @IBOutlet weak var termsHyperlinkTextView: UITextView! {
                  didSet {
                      termsHyperlinkTextView.delegate = self
                      termsHyperlinkTextView.hyperLink(originalText: "Check out terms & conditions or our privacy policy",
                                                       linkTextsAndTypes: ["terms & conditions": LinkType.termsAndConditions.rawValue,
                                                                           "privacy policy": LinkType.privacyPolicy.rawValue])
          
                  }
              }
          
          enum LinkType: String {
                  case termsAndConditions
                  case privacyPolicy
              }
          
          // MARK: - UITextViewDelegate
          extension ViewController: UITextViewDelegate {
              func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
                  if let linkType = LinkType(rawValue: URL.absoluteString) {
                      // TODO: handle linktype here with switch or similar.
                  }
                  return false
              }
          }
          

          【讨论】:

            【解决方案10】:

            您可以使用这个简单的方法为任何以 tag

            开头的字符集添加超链接
            func addLink(forString string : NSMutableAttributedString
                    ,baseURL : String
                    ,tag : String){
                    let array = string.string.replacingOccurrences(of: "\n", with: " ").components(separatedBy: " ")
                    let filterArray = array.filter { (string) -> Bool in
                        return string.contains(tag)
                    }
                    for element in filterArray {
                        let removedHashtag = element.replacingOccurrences(of: tag, with: "")
                        let url = baseURL + removedHashtag
                        let range = NSString.init(string: (string.string)).range(of: element)
                        string.addAttributes([NSAttributedStringKey.link : url.replacingOccurrences(of: " ", with: "")], range: range)
                    }
                }
            

            【讨论】:

              【解决方案11】:

              我想做同样的事情,最后只使用了一个标题为“单击此处”的 UIButton,周围是 UILabel “just”和“to register”,然后:

              @IBAction func btnJustClickHereLink(_ sender: UIButton) {
                  if let url = URL(string: "http://example.com") {
                      UIApplication.shared.openURL(url)
                  }
              }
              

              【讨论】:

              • 这不是一个合适的解决方案,因为它可能无法扩展到不同的设备尺寸,过多的标签和按钮组合和约束可能会中断。另外,如果单个测试有多个链接怎么办?然后会添加多少按钮和标签?带有属性字符串的文本字段或文本视图是正确的解决方案。
              猜你喜欢
              • 1970-01-01
              • 2020-05-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2015-11-28
              • 2012-07-06
              • 2018-12-23
              • 1970-01-01
              相关资源
              最近更新 更多