11

i'm quite new to iOS Development and right now working on an app which receive some kind of JSON Data. But some Backend Experts thought, that it would be better for the User if they just copy the Information straight out of Word and paste it into the information System. So I'm sitting here, trying to make a clickable Link in a UITableView.

I parse the data from Web and get a String with this format:

F&uuml;r mehr Informationen klicken sie <a href="http://www.samplelink.com/subpage.php?id=8">here</a>.

I tried already a UILabel, but after some research I use now the often suggested UITextView. In the Attributed Inspector, i set it as an Attributed Text and enabled the Link Detection. Now the text is shown red and is clickable.

The Problem now for me is, that the HTML Tags and the correct (German) Character Set is still missing and i got no idea, how to display it in the right way.

The shown string is parsed in this way:

    func showHTMLString(unformattedString: String) -> NSAttributedString{
    var attrStr = NSAttributedString(
        data: tmpString.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!,
        options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
        documentAttributes: nil,
        error: nil)
    return attrStr!
}

If i fill the Textview with attrStr?.string the Format is shown in the correct way but the link is gone as well.

Any suggestions how to format the shown string in the right way?

Thanks in advance AR4G4

a2hur
  • 167
  • 1
  • 1
  • 11
  • Can you explain exactly what you are wanting the output to look like? For instance is your expected output "Für mehr Informationen klicken sie here." with the here being a clickable link? – Jeremy Pope Feb 09 '15 at 18:27
  • Thats exactly what i want – a2hur Feb 09 '15 at 20:34

8 Answers8

26

The problem there is that you have to change the Character Encoding options from NSUnicodeStringEncoding to NSUTF8StringEncoding to load your of your html the proper way. I think you should create a string extension read-only computed property to convert your html code to attributed string:

Xcode 8.3.1 • Swift 3.1

extension Data {
    var attributedString: NSAttributedString? {
        do {
            return try NSAttributedString(data: self, options:[NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
        } catch {
            print(error)
        }
        return nil
    }
}
extension String {
    var data: Data {
        return Data(utf8)
    }
}

let htmlStringCode = "F&uuml;r mehr Informationen klicken sie <a href=\"http://www.samplelink.com/subpage.php?id=8\">here</a>"

htmlStringCode.data.attributedString?.string ?? ""  // "Für mehr Informationen klicken sie here"

in your case

yourTextView.attributedText = htmlStringCode.data.attributedString
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • This seems like the way to go, especially if you'll be doing it a lot. – Jeremy Pope Feb 09 '15 at 18:35
  • thanks @Leonardo Savio Dabus, with your hint, everything is working fine now! just to need reassign the cell but the Link and Display is now shown exactly how i wanted it. – a2hur Feb 09 '15 at 20:52
  • @AR4G4 Why did you unaccepted the answer? The question is how to format HTML to display in UITextView. – Leo Dabus Feb 18 '15 at 22:49
  • sry, i'm new here. thought i can accept more than one answer. – a2hur Feb 19 '15 at 09:06
  • This is no longer working. `Cannot invoke initializer for type 'NSAttributedString' with an argument list of type '(data: NSData, options: NSDictionary, documentAttributes: _, error: _)'` – Legoless Feb 22 '16 at 15:29
  • @Legoless http://stackoverflow.com/questions/28124119/convert-html-to-plain-text-in-swift/28132610?s=1|0.2249#28132610 – Leo Dabus Feb 22 '16 at 15:32
  • `NSUnicodeStringEncoding` is preferred in case of CJK character, because NSString is Unicode encoded, instead of UTF-8 – DawnSong Apr 06 '16 at 11:04
  • @DawnSong you can extend NSData instead of String and use any String Encoding. See my edit – Leo Dabus Apr 06 '16 at 17:57
  • Thank you for your answer. As we know,`NSString` is already `NSUTF16StringEncoding`, which is equal to `NSUnicodeStringEncoding`, and you have no need to convert the string to UTF-8 data and parse data using UTF-8, except that what you get is UTF-8 data instead of NSString. As Apple said in "NSString Class Reference". – DawnSong Apr 07 '16 at 09:19
  • You should always work with Swift native type String . – Leo Dabus Apr 07 '16 at 14:00
  • For everyone using Swift 3: Xcode converts NSUTF8StringEncoding to String.Encoding.utf8. This will crash. String.Encoding.utf8.rawValue is expected as the attributes value. – mangerlahn Aug 18 '16 at 08:52
  • Since HTML parsing happens in the main thread and you're using in a table view, I'd definitely cache that attributed string so you only have to parse a specific string one time. NSAttributedString with NSHTMLTextDocumentType is an expensive operation and you will be dropping frames for doing that. – tperei Sep 06 '16 at 22:54
  • since this answer got just mentioned, could you @LeoDabus have a look here please. http://stackoverflow.com/questions/43357701/how-to-transfer-html-text-into-an-attributed-string-without-losing-line-breaks-i?noredirect=1#comment73778668_43357701 – David Seek Apr 12 '17 at 00:02
  • @LeoDabus it takes long time to load because it convert html content to NSData that by it taking time What is the solution for. – Pintu Rajput Nov 30 '17 at 06:56
2

Check the attributes of your UITextView in IB. In order for the links to work, you must have Selectable checked.

enter image description here

fred02138
  • 3,323
  • 1
  • 14
  • 17
  • jep, i tried it before with many variations. now i got selectable and Link Detection enabled. – a2hur Feb 09 '15 at 20:54
1

I would recommend displaying HTML in a UIWebView. It is more robust than using a UITextView. See Display html text in uitextview for more information.

Community
  • 1
  • 1
tng
  • 4,286
  • 5
  • 21
  • 30
  • tried it as well and works also. 'webView.loadHTMLString(myString.string, baseURL: nil)' shows the link and Encoding also in the right way. but what do you mean with more robust? I thought using a UIWebView in every UITableViewCell could be a little bit too much. but like i said, i'm new to iOS Dev and have no idea, which is the best practice – a2hur Feb 09 '15 at 21:02
  • It's more robust in the sense that it's a real HTML renderer. For example, it can handle CSS style sheets, it can run JavaScript, etc. I'm not sure what kind of HTML you expect to render - for simple things, the UITextView may work. – tng Feb 09 '15 at 22:55
  • hey tng, the webview seem the better solution (loads faster) but i'm stucked now, that the link opens within the small webview. looked up over here: [link](http://stackoverflow.com/questions/2899699/uiwebview-open-links-in-safari) but where do i need to put the shouldStartLoadWithRequest? Made now a 'class CustomUIWebViewController: UIWebView, UIWebViewDelegate' and put the function in there, but the links still open in the WebView. (My Rep is too low, to comment on the other Thread) thank you – a2hur Feb 10 '15 at 18:24
  • Implement the `UIWebViewDelegate` and then handle `shouldStartLoadRequest` - see https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIWebViewDelegate_Protocol/index.html#//apple_ref/occ/intfm/UIWebViewDelegate/webView:shouldStartLoadWithRequest:navigationType: – tng Feb 11 '15 at 19:39
  • When you implement it, you can choose to continue to open the link in the current window, or you can navigate to a different view, or you can even open the link in Safari. It's up to you how you want to handle the links. – tng Feb 11 '15 at 19:40
  • UIWebView is deprecated now, use WebKit View instead – pragmus Apr 12 '19 at 08:24
1

I used code for Swift 4:

var descriptionStr : String = String() //Dynamic text

let regex = try! NSRegularExpression(pattern: "<.*?>", options: [.caseInsensitive])
        let range = NSRange(location: 0, length: descriptionStr.count)
        let htmlLessString: String = regex.stringByReplacingMatches(in: descriptionStr, options: NSRegularExpression.MatchingOptions(), range:range, withTemplate: "")
        textViewRef.text = htmlLessString
Mannam Brahmam
  • 2,225
  • 2
  • 24
  • 36
0

Having created your attributed string, you would then set the attributedText property of the UITextView to be the NSAttributedString itself, not the string property of that attributed string.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
0

Your versions is pretty close to begin with. As Leonardo Savio Dabus stated you should probably try NSUTF*StringEncoding. The following produces your expected output for me. As he said, you might want to add it to an extension of string, if you are doing this a lot.

    let theString = "F&uuml;r mehr Informationen klicken sie <a href=\"http://www.samplelink.com/subpage.php?id=8\">here</a>."
    let theAttributedString = NSAttributedString(data: str.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!,
                                                 options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil, error: nil)
    theTextView.attributedText = atString
Jeremy Pope
  • 3,342
  • 1
  • 16
  • 17
0

Another way I used to do this :

var someHtmlString = "F&uuml;r mehr Informationen klicken sie <a href=\"http://www.samplelink.com/subpage.php?id=8\">here</a>."
let regex = try! NSRegularExpression(pattern: "<.*?>", options: [.CaseInsensitive])
let range = NSRange(location: 0, length: someHtmlString.characters.count)
let htmlLessString: String = regex.stringByReplacingMatchesInString(someHtmlString, options: NSMatchingOptions(), range:range, withTemplate: "")

End result -> htmlLessString is

"F&uuml;r mehr Informationen klicken sie here."
JAL
  • 41,701
  • 23
  • 172
  • 300
Nebojsa Nadj
  • 621
  • 3
  • 12
0

I had an app that had a UITextView where I wanted to be able to paste html formatted text from the browser and then save it as a string(containing html formatting) to the database, and then another time retrieve it from the database and show it with the same format as it was first copied from the website. I managed this by making these two extensions:

extension String
{
    func getAttributedStringFromHTMLString() -> NSAttributedString
    {
        do {
            let attributedString = try NSAttributedString(data: self.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!, options: [NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType], documentAttributes: nil)
            return attributedString
        } catch {
            print(error)
            return NSAttributedString()
        }
    }
}

extension NSAttributedString
{
    func getHTMLString() -> String
    {
        var htmlText = "";
        let documentAttributes = [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType]
        do {
            let htmlData = try self.dataFromRange(NSMakeRange(0, self.length), documentAttributes:documentAttributes)
            if let htmlString = String(data:htmlData, encoding:NSUTF8StringEncoding) {
                htmlText = htmlString
            }
            return htmlText
        }
        catch {
            print("error creating HTML from Attributed String")
            return ""
        }
    }
}