Anuranjan Bose
Anuranjan Bose

Reputation: 231

How to display HTML text(as string) on UILabel with hyperlinks?

I want to display a string(which is actually a small HTML code, with hyperlink) on UILabel. E.g. The string which I have to display is: "Click here to know more". So is there a way to display it on UILabel, and on clicking the hyperlink(Click here) it opens up the desired web page? Here's what I did:

@IBOutlet weak var testLabel: UILabel!

override func viewDidLoad() {
   super.viewDidLoad()
   let htmlData = NSString(string: "<a href=\"http://www.google.com\">Click here</a> to know more").data(using: String.Encoding.unicode.rawValue)
   let attributedString = try? NSAttributedString(data: htmlData!, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil)
   testLabel.attributedText = attributedString
}

The label displayed the string just like I wanted, but on clicking the hyperlink, it didn't do what what desired, i.e. open a web page.

Upvotes: 2

Views: 4043

Answers (3)

Anuranjan Bose
Anuranjan Bose

Reputation: 231

@IBOutlet weak var testLabel: UILabel!

let message = "Please <a href='https://www.google.com'>click here</a> to search"

@override func viewDidLoad() {
   super.viewDidLoad()
   formatLabel(with: message.htmlToString)
}

func formatLabel(with message: String) {
   let formattedText = String.format(strings: ["click here"],
                                     boldFont:  UIFont.init(name: "Roboto-Bold", size: 16.0)!,
                                     boldColor: UIColor.blue,
                                     inString: message,
                                     font: UIFont.init(name: "Roboto-Regular", size: 16.0)!,
                                     color: UIColor(red: 33/255, green: 136/255, blue: 189/255, alpha: 1.0))
   testLabel.attributedText = formattedText
   testLabel.numberOfLines = 0
   let tap = UITapGestureRecognizer(target: self, action: #selector(handleTermTapped))
   testLabel.addGestureRecognizer(tap)
   testLabel.isUserInteractionEnabled = true
   testLabel.textAlignment = .center
}

@objc func handleTermTapped(gesture: UITapGestureRecognizer) {
   let clickString = (testLabel.text ?? "") as NSString
   let clickRange = clickString.range(of: "click here")

   let tapLocation = gesture.location(in: testLabel)
   let index = testLabel.indexOfAttributedTextCharacterAtPoint(point: tapLocation)

   if checkRange(clickRange, contain: index) == true {
       guard let url = URL(string: "https://www.google.com") else { return }
       UIApplication.shared.open(url, options: [:], completionHandler: nil)
       return
   }

}

Helper function and extensions:


func checkRange(_ range: NSRange, contain index: Int) -> Bool {
    return index > range.location && index < range.location + range.length
}

extension String {
    static func format(strings: [String],
                       boldFont: UIFont = UIFont.init(name: "Roboto-Bold", size: 16.0)!,
                       boldColor: UIColor = UIColor.blue,
                       inString string: String,
                       font: UIFont = UIFont.init(name: "Roboto-Regular", size: 16.0)!,
                       color: UIColor = UIColor(red: 33/255, green: 136/255, blue: 189/255, alpha: 1.0)) -> NSAttributedString {
        let attributedString =
            NSMutableAttributedString(string: string,
                                      attributes: [
                                        NSAttributedString.Key.font: font,
                                        NSAttributedString.Key.foregroundColor: color])
        let boldFontAttribute = [NSAttributedString.Key.font: boldFont, NSAttributedString.Key.foregroundColor: boldColor]
        for bold in strings {
            attributedString.addAttributes(boldFontAttribute, range: (string as NSString).range(of: bold))
        }
        return attributedString
    }

   var htmlToAttributedString: NSAttributedString? {
        guard let data = data(using: .utf8) else { return NSAttributedString() }
        do {
            return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue], documentAttributes: nil)
        } catch {
            return NSAttributedString()
        }
    }

    var htmlToString: String {
        return htmlToAttributedString?.string ?? ""
    }
}

extension UILabel {
    func indexOfAttributedTextCharacterAtPoint(point: CGPoint) -> Int {
        let textStorage = NSTextStorage(attributedString: self.attributedText!)
        let layoutManager = NSLayoutManager()
        textStorage.addLayoutManager(layoutManager)
        let textContainer = NSTextContainer(size: self.frame.size)
        textContainer.lineFragmentPadding = 0
        textContainer.maximumNumberOfLines = self.numberOfLines
        textContainer.lineBreakMode = self.lineBreakMode
        layoutManager.addTextContainer(textContainer)

        let index = layoutManager.characterIndex(for: point, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
        return index
    }
}

Upvotes: 1

Asperi
Asperi

Reputation: 257493

Here is simple demo of how to do what you need with UILabel

import UIKit

class ViewController : UIViewController {
    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white

        let label = UILabel()
        label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
        label.attributedText = NSAttributedString(string: "Tap Me", attributes: [.link : URL(string: "http://www.google.com")!])
        label.isUserInteractionEnabled = true
        label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleLink(_:))))
        view.addSubview(label)
        self.view = view
    }

    @IBAction func handleLink(_ sender: UIGestureRecognizer) {
        guard sender.state == .ended else { return }

        guard let label = sender.view as? UILabel else { return }

        guard let link = label.attributedText?.attribute(.link, at: 0, effectiveRange: nil) as? URL else { return }

        UIApplication.shared.open(link, options: [:], completionHandler: nil)
    }
}

Upvotes: 0

Moayad Al kouz
Moayad Al kouz

Reputation: 1392

Try to use UITextView instead of UILabel

    @IBOutlet weak var testTextView: UITextView!

    override func viewDidLoad() {
       super.viewDidLoad()
       testTextView.isEditable = false

       testTextView.dataDetectorTypes = .link
       testTextView.isScrollEnabled = false
       let htmlData = NSString(string: "<a href=\"http://www.google.com\">Click here</a> to know more").data(using: String.Encoding.unicode.rawValue)
       let attributedString = try? NSAttributedString(data: htmlData!, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil)
       testTextView.attributedText = attributedString
    }

Upvotes: 4

Related Questions