Reputation: 231
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
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
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
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