HTML Formatting not working with UILabel in swift - html

I have used this code to formatting HTML content on UILabel
var htmlToAttributedString: NSAttributedString? {
do {
guard let data = data(using: String.Encoding.utf8) else {
return nil
}
return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil)
} catch {
print("error: ", error)
return nil
}
}
My HTML Content Like:
("<p>My Name is <b>Ayush</b> <I>Bansal</I></p>")
When I used above code for formatting then I get output like (My Name is Ayush Bansal).
But I want my output will be like this (My Name is Ayush Bansal)

you can use this extension
extension String {
func convertToAttributed(_ defaultFont: UIFont, textColor: UIColor ) -> NSAttributedString? {
guard let data = "".replacingOccurrences(of: "\n", with: "<br>").data(using: .utf8, allowLossyConversion: false) else { return nil }
guard let attributedString = try? NSMutableAttributedString(data: data,
options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html],
documentAttributes: nil)
else { return nil }
attributedString.enumerateAttribute(NSAttributedString.Key.font,
in: NSRange(location: 0, length: attributedString.length),
options: NSAttributedString.EnumerationOptions(rawValue: 0))
{ (value, range, _) -> Void in
if let oldFont = value as? UIFont {
var newFont = defaultFont
let isBold = oldFont.fontName.lowercased().contains("bold")
let isItalic = oldFont.fontName.lowercased().contains("italic")
if isBold && isItalic {
newFont = UIFont.bolditalicFont(ofSize: 14)
} else if isBold {
newFont = UIFont.boldFont(ofSize: 14)
} else if isItalic {
newFont = UIFont.italicFont(ofSize: 14)
}
attributedString.removeAttribute(NSAttributedString.Key.font, range: range)
attributedString.addAttribute(NSAttributedString.Key.font, value: newFont, range: range)
attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: textColor, range: range)
}
}
}
}
usage:
yourLabel.attributedText = string.convertToAttributed(font, textColor: textcolor)

Related

NSAttributedString change color and font size

Hi guys I have small problem, I'm using func:
static func convertFromHTMLString(_ input: String?) -> NSAttributedString? {
guard let input = input else { return nil }
guard let data = input.data(using: String.Encoding.unicode, allowLossyConversion: true) else { return nil }
return try? NSAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html], documentAttributes: nil)
}
to read my Strings from Localizable.strings with attributes like this:
But when I run my app it looks that:
This change my Label color to black and font size to something like 10-12 ;/
My Label should have white color and font size 17, anyone know how to fix it?
Thanks ! :)
#Edit1
The solution must look like this
This is how it looks on Android.
func convertFromHTMLString(_ input: String?) -> NSAttributedString? {
guard let input = input else { return nil }
guard let data = input.data(using: String.Encoding.unicode, allowLossyConversion: true) else { return nil }
if let string = try? NSAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html], documentAttributes: nil).string
{
//Set color and font
let myAttribute = [ NSAttributedString.Key.foregroundColor: UIColor.white , NSAttributedString.Key.font: UIFont(name: "Chalkduster", size: 17.0)! ]
let myAttrString = NSAttributedString(string: string, attributes: myAttribute)
return myAttrString
}
return nil
}

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: "Click here 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.
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: "Click here 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
}
#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
}
}
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)
}
}

how to show image in swift

Imagine I have this json:
I could convert Json to string and show it by below code:
extension String {
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 ?? ""
}
}
but I can’t display the image in json. How can I do that?

How to remove whitespace between two paragraphs?

My image
I am getting data from Html to the text.
Now please tell me how to avoid the space between two paragraphs ?
func processGetDescriptionResponse(json: JSON) {
let status = json.dictionaryObject!["status"] as? Bool
if status == true {
let data = json.dictionaryValue["data"]!
let values = data.arrayValue
dataSourceDescription.removeAll()
for i in 0 ..< values.count {
let description = Description.initWithJSON(values[i])
dataSourceDescription.append(description)
// DetailDescription.text = dataSourceDescription[i].content
if dataSourceDescription[i].file_url == ""
{
DetailImage.image = UIImage(named: "logo_two_fifty")
}
else
{
let imageURL = NSURL(string: baseURLString + dataSourceDescription[i].file_url!)
DetailImage.kf_setImageWithURL(imageURL!, placeholderImage: nil, optionsInfo: [.Transition(ImageTransition.Fade(50))]) { (image, error, cacheType, imageURL) in
}
}
DetailDescription.attributedText = convertText(dataSourceDescription[i].content!)
print(DetailDescription.text)
}
} else {
}
}
If you receive html content, you can display it in UITextView using NSAttributedString:
let htmlString = "<html><body><p>Para 1</p><p>Para 2</p></body></html>"
let attributedString = try!
NSAttributedString(data: htmlString.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: false)!,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
documentAttributes: nil)
yourTextView.attributedText = attributedString

Swift: How do I return a value within an asynchronous urlsession function?

As you can see, I'm receiving a JSON file, parsing it using SwiftyJSON, and trying to return totalTime, but it won't let me. How do I do this?
func googleDuration(origin: String, destination: String) -> Int{
// do calculations origin and destiantion with google distance matrix api
let originFix = origin.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil);
let destinationFix = destination.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil);
let urlAsString = "https://maps.googleapis.com/maps/api/distancematrix/json?origins="+originFix+"&destinations="+destinationFix;
println(urlAsString);
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let task = urlSession.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
if error != nil {
// If there is an error in the web request, print it to the console
println(error.localizedDescription)
}
println("parsing JSON");
let json = JSON(data: data);
if (json["status"].stringValue == "OK") {
if let totalTime = json["rows"][0]["elements"][0]["duration"]["value"].integerValue {
println(totalTime);
}
}
})
task.resume();
}
You should add your own completionHandler closure parameter and call it when the task completes:
func googleDuration(origin: String, destination: String, completionHandler: (Int?, NSError?) -> Void ) -> NSURLSessionTask {
// do calculations origin and destiantion with google distance matrix api
let originFix = origin.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil);
let destinationFix = destination.stringByReplacingOccurrencesOfString(" ", withString: "+", options: NSStringCompareOptions.LiteralSearch, range: nil);
let urlAsString = "https://maps.googleapis.com/maps/api/distancematrix/json?origins="+originFix+"&destinations="+destinationFix
println(urlAsString)
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let task = urlSession.dataTaskWithURL(url) { data, response, error -> Void in
if error != nil {
// If there is an error in the web request, print it to the console
// println(error.localizedDescription)
completionHandler(nil, error)
return
}
//println("parsing JSON");
let json = JSON(data: data)
if (json["status"].stringValue == "OK") {
if let totalTime = json["rows"][0]["elements"][0]["duration"]["value"].integerValue {
// println(totalTime);
completionHandler(totalTime, nil)
return
}
let totalTimeError = NSError(domain: kAppDomain, code: kTotalTimeError, userInfo: nil) // populate this any way you prefer
completionHandler(nil, totalTimeError)
}
let jsonError = NSError(domain: kAppDomain, code: kJsonError, userInfo: nil) // again, populate this as you prefer
completionHandler(nil, jsonError)
}
task.resume()
return task
}
I'd also have this return the NSURLSessionTask in case the caller wants to be able to cancel the task.
Anyway, you'd call this like so:
googleDuration(origin, destination: destination) { totalTime, error in
if let totalTime = totalTime {
// use totalTime here
} else {
// handle error
}
}
Another example:
class func getExchangeRate(#baseCurrency: String, foreignCurrency:String, completion: ((result:Double?) -> Void)!){
let baseURL = kAPIEndPoint
let query = String(baseCurrency)+"_"+String(foreignCurrency)
var finalExchangeRate = 0.0
if let url = NSURL(string: baseURL + query) {
NSURLSession.sharedSession().dataTaskWithURL(url) { data, response, error in
if ((data) != nil) {
let jsonDictionary:NSDictionary = NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as NSDictionary
if let results = jsonDictionary["results"] as? NSDictionary{
if let queryResults = results[query] as? NSDictionary{
if let exchangeRate = queryResults["val"] as? Double{
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
dispatch_async(dispatch_get_main_queue()) {
completion(result: exchangeRate)
}
}
}
}
}
}
else {
completion(result: nil)
}
}.resume()
}
}
Call:
Currency.getExchangeRate(baseCurrency: "USD", foreignCurrency: "EUR") { (result) -> Void in
if let exchangeValue = result {
print(exchangeValue)
}
}
Another example:
func getJason(url: NSURL, completionHandler: (String?, NSError?) -> Void ) -> NSURLSessionTask {
var finalData: String!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if error != nil{
completionHandler(nil, error)
return
}
else{
if let urlContent = data{
do{
let jsonData = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers)
if let ip = jsonData["ip"]{
finalData = ip as? String
completionHandler(finalData, nil)
return
}
}catch{
print("EMPTY")
}
}
}
}
task.resume()
return task
}
Then i called it in the viewDidLoad
getJason(url) { (ipAddress, error) -> Void in
if error != nil{
print(error)
}
else{
if let ip = ipAddress{ //To get rid of optional
self.ipLabelDisplay.text = "Your Ip Address is: \(ip)"
}
}
}