1

I can't believe I am asking this question but (been looking for the answer for an hour and a half with no luck)...how do I append NSAttributedText to a UITextView? (in Swift 2.0+)

I'm building a tool which downloads items from my server, and as they come in I want to add AttributedText with green for success or red color for fails.

To do this I believe I need NSMutableAttributedString, but the UITextView only has NSattributedString which does not have access to the appendAttributedString(attrString: NSAttributedString NSAttributedString)

So if I have a UITextView with an NSAttributedString on it that says "loading" in red, how can I append the text "loading" with the text in green "success".

For example like this:

<font color="red">loading</font><font color="green">success</font>

Update

I found an answer to my question but I do not feel is an optimal answer.

let loadingMessage = NSMutableAttributedString(string: "loading...\n")
            loadingMessage.addAttribute(NSStrokeColorAttributeName, value: UIColor.redColor(), range: NSRange(location: 0, length: 10))

            progressWindowViewController.theTextView.attributedText = loadingMessage

loadingMessage.appendAttributedString("<font color=\"#008800\">Successfully Synced Stores...</font>\n".attributedStringFromHtml!)
                progressWindowViewController.theTextView.attributedText = loadingMessage

My answer above works but does so by overwriting the entire text (and it will continue to do so every-time it draws). I wonder if there is a true way to append the string to the end for optimal performance?

The extension I used for HTML

extension String {

    var attributedStringFromHtml: NSAttributedString? {
        do {
            return try NSAttributedString(data: self.dataUsingEncoding(NSUTF8StringEncoding)!, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
        } catch _ {
            print("Cannot create attributed String")
        }
        return nil
    }
}
Joseph Astrahan
  • 8,659
  • 12
  • 83
  • 154

2 Answers2

4

You can convert a NSAttributedString to a NSMutableAttributedString using mutableCopy(), and copy() will do the opposite for you, like so:

let string1 = NSAttributedString(string: "loading", attributes: [NSForegroundColorAttributeName: UIColor.redColor()])
let string2 = NSAttributedString(string: "success", attributes: [NSForegroundColorAttributeName: UIColor.greenColor()])

let newMutableString = string1.mutableCopy() as! NSMutableAttributedString
newMutableString.appendAttributedString(string2)

textView.attributedText = newMutableString.copy() as! NSAttributedString

It's only a bit awkward since both mutableCopy() and copy() return AnyObject, so you'll need to convert them using as! to the correct type all the time.

Lasse
  • 490
  • 3
  • 8
  • Your method is really interesting, I updated my question with another solution I found, is yours more optimal? Like if I keep appending strings after I've set it to the textView will it append or do I have to keep resetting the string on the textView? – Joseph Astrahan Apr 19 '16 at 00:22
  • 1
    I wouldn't want to judge which method is more effective, it certainly depends on context. But performance-wise I would guess that using `mutableCopy()` and `copy()` ist pretty fast. Unfortunately you won't be able to do a "live append" to the UITextViews.attributedText since this is an immutable object. (You could, since `NSMutableAttributedString` is a subclass of `NSAttributedString`, assign a mutable string to the UITextView. However, invoking `appendAttributedString()` does not work here - it seems like UITextView is doing some internal conversion) – Lasse Apr 19 '16 at 00:40
  • 1
    your answer helped, the only difference was I defined the strings as NSMutableAttributedString to begin with to avoid the conversions. Unfortunate that UITextview does not have internal way to append properly. – Joseph Astrahan Apr 19 '16 at 00:49
  • How would I use an already formated string from `textView.attributedText` and append something to that? – powtac Oct 11 '17 at 20:05
1
    let loadingMessage = NSMutableAttributedString(string: "loading...")
    loadingMessage.addAttribute(NSStrokeColorAttributeName, value: UIColor.redColor(), range: NSRange(location: 0, length: 10))

    let textView = UITextView()
    textView.attributedText = loadingMessage
  • This does not append the word success though? That is the key part to this question. This just shows how to make the text red in a range which I already know how to do. Also keep in mind I know how to append normal text, but in this case we are going to append 'green' text that says success. This is the hard part to figure out. – Joseph Astrahan Apr 18 '16 at 23:41