How to display HTML in UILabel and UITextView

When you work with an API, there would be a time when your backend wants to control a text style. HTML is the most common format for this kind of job. Do you know that WKWebView is not the only way to present HTML string? You can render HTML string directly on UILabel and UITextView.

HTML String #

A backend might return a response with something like this:

"This is a <b>bold</b> text."

Convert to NSAttributedString #

To render this text properly in UILabel or UITextView, you need to convert it to NSAttributedString . NSAttributedString has built-in support for this conversion.

First, we need to convert HTML string to Data .

let htmlString = "This is a <b>bold</b> text."

let data = htmlString . data ( using : . utf8 ) !

Then we initialize NSAttributedString with the data and documentType option to .html .

let attributedString = try ? NSAttributedString (

data : data ,

options : [ . documentType : NSAttributedString . DocumentType . html ] ,

documentAttributes : nil )

After you get an attributed string, you can set it to UILabel or UITextView.

label . attributedText = attributedString

The result is quite ugly, as you might be expected.

Default style for html string

Before we fix the style problem, let's create an extension out of this.

extension String {

func htmlAttributedString ( ) - > NSAttributedString ? {

guard let data = self . data ( using : . utf8 ) else {

return nil

}



return try ? NSAttributedString (

data : data ,

options : [ . documentType : NSAttributedString . DocumentType . html ] ,

documentAttributes : nil

)

}

}

Now, we can use it like this.

let htmlString = "This is a <b>bold</b> text."

label . attributedText = htmlString . htmlAttributedString ( )

Apply a style #

Since we are dealing with HTML, we can use CSS to apply a style to our attributed string. Simply create HTML boilerplate string and interpolate our content inside a body tag.

func htmlAttributedString ( ) - > NSAttributedString ? {

let htmlTemplate = "" "

< ! doctype html >

< html >

< head >

< style >

body {

font - family : - apple - system ;

font - size : 24px ;

}

< / style >

< / head >

< body >

\ ( self )

< / body >

< / html >

"" "



guard let data = htmlTemplate . data ( using : . utf8 ) else {

return nil

}



guard let attributedString = try ? NSAttributedString (

data : data ,

options : [ . documentType : NSAttributedString . DocumentType . html ] ,

documentAttributes : nil

) else {

return nil

}



return attributedString

}

Attributed string with CSS as a style

You can make our extension accept parameters of size and color for the ease of use.

extension String {

func htmlAttributedString ( size : CGFloat , color : UIColor ) - > NSAttributedString ? {

let htmlTemplate = "" "

< ! doctype html >

< html >

< head >

< style >

body {

color : \ ( color . hexString ! ) ;

font - family : - apple - system ;

font - size : \ ( size ) px ;

}

< / style >

< / head >

< body >

\ ( self )

< / body >

< / html >

"" "



guard let data = htmlTemplate . data ( using : . utf8 ) else {

return nil

}



guard let attributedString = try ? NSAttributedString (

data : data ,

options : [ . documentType : NSAttributedString . DocumentType . html ] ,

documentAttributes : nil

) else {

return nil

}



return attributedString

}

}



extension UIColor {

var hexString : String ? {

if let components = self . cgColor . components {

let r = components [ 0 ]

let g = components [ 1 ]

let b = components [ 2 ]

return String ( format : "#%02x%02x%02x" , ( Int ) ( r * 255 ) , ( Int ) ( g * 255 ) , ( Int ) ( b * 255 ) )

}

return nil

}

}

You can use it like this:

label . attributedText = htmlString . htmlAttributedString2 ( size : 18 , color : . systemPink )

One thing you should know about this is you have to call this initializer from the main thread. From Apple Documentation The HTML importer should not be called from a background thread (that is, the options dictionary includes documentType with a value of html). It will try to synchronize with the main thread, fail, and time out. Calling it from the main thread works (but can still time out if the HTML contains references to external resources, which should be avoided at all costs). The HTML import mechanism is meant for implementing something like markdown (that is, text styles, colors, and so on), not for general HTML import.

Related Resources #

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron

Tweet

Share

← Home