3
votes

I'm having trouble getting attributedText to manage fonts and bold together. My scenario is a view controller with a scrollable text view that accept attributed text.

The attributes are created by converting a simple html string (ul's and b's) with the following function below.

Thats all fine. The problem appears in my view controller when I later set the text to be the attributedData and then set the font.

Doing text, then font: removes the bold from being displayed. Doing font, then text: stops the font from working.

My suspicion is that bold isn't represented in the string as "bold", but as using the font-bold.
When I set the text, then font, it's overriding any fonts in the attributes (which includes the bold). When I set the font, then text, the font in the attributes is the last one, and it was set up with the default font but correctly switches to the bold version of the font.

Is there a way to change the font on an attributedString and maintain the bold/italics? Not my preferred place, but can the font be set in the options of the NSAttributedString function?

func html2AttributedString(fromString: String) -> NSAttributedString {
    var returnString: NSAttributedString

    do {
        let attributedData = fromString.dataUsingEncoding(NSUnicodeStringEncoding)
        let tempAttributedString = try NSAttributedString(data: attributedData!, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
        returnString = tempAttributedString
    } catch {
        let tempString = "Unknown String"
        let attributedData = tempString.dataUsingEncoding(NSUnicodeStringEncoding)
        let tempAttributedString = try! NSAttributedString(data: attributedData!, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
        returnString = tempAttributedString
    }

    return returnString
}

Setting the text in View Controller

    contentTextView.attributedText = descriptionTextAttributed
    contentTextView.textColor = UIColor.blackColor()
    if let font = UIFont(name: "StoneSans", size: 20.0) {
        contentTextView.font = font
    }

Update: Thanks Wain for his comments.

There's three levels I can see for changing the font: html, when converting and when it's finished converting.

Wain has come up with the goods to change it within the attributedText after it has been converted, but I am also CPU limited and that methods going to be noticeably slow for the user.

I've tried doing it programmatically for the html but it doesn't work. Maybe my insert needs fixing if anyone can help:

    //let tempString = "<style> {font-family: sans-serif;}</style>" + fromString
    //let tempString = "<body style='font-family=sans-serif'>" + fromString

I'm currently trying to get the syntax right to try and change the attributes used during conversion (see How do you use NSAttributedString?) using documentAttributes. This code is not compiling as I'm not preparing the documentAttributes properly:

    if let font = UIFont(name: "StoneSans", size: CGFloat(AppData.sharedInstance.instructionFontSize)) {
        do {
            let attributedData = fromString.dataUsingEncoding(NSUnicodeStringEncoding)
            let myAttributes:Dictionary = [
                    NSParagraphStyleAttributeName : NSMutableParagraphStyle(),
                    NSKernAttributeName : 3, // (-1,5)
                    NSFontAttributeName : font,
                    NSForegroundColorAttributeName : UIColor.whiteColor(),
                    NSShadowAttributeName : nil
            ]
            let tempAttributedString = try NSAttributedString(data: attributedData!, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: myAttributes)
            returnString = tempAttributedString
        } catch {
            let tempString = "Unknown String"
            let attributedData = tempString.dataUsingEncoding(NSUnicodeStringEncoding)
            let tempAttributedString = try! NSAttributedString(data: attributedData!, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
            returnString = tempAttributedString
        }
    }

If anyone can fix my code for the two earlier methods of changing font it would be appreciated.

Tim.

1

1 Answers

2
votes

You're right, the bold and italic information is part of the font information. Using NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType uses a default font (or whatever font is specified in the HTML).

So, you need to edit the fonts rather than just replacing them, with something like:

NSMutableAttributedString *attributedString = [self mutableCopy];

[attributedString beginEditing];
[attributedString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, attributedString.length) options:0 usingBlock:^(UIFont *value, NSRange range, BOOL *stop) {

    NSString *fontName = [value.fontName lowercaseString];
    UIFont *destinationFont = [UIFont XXXX];

    if ([fontName rangeOfString:@"bold"].location != NSNotFound) {
        destinationFont = [UIFont BOLD_XXXX];
    } else if ([fontName rangeOfString:@"italic"].location != NSNotFound) {
        destinationFont = [UIFont ITALIC_XXXX];
    }

    [attributedString removeAttribute:NSFontAttributeName range:range];
    [attributedString addAttribute:NSFontAttributeName value:destinationFont range:range];
}];
[attributedString endEditing];