create an attributed string out of plain (Android

2019-03-20 06:42发布

I am reading strings out of a Localizable.strings which contains something like that which is basically what you have in an strings.xml of an Android app

"testShort" = "A <b>short</b>\ntest with another<b>bold text</b>";

The bold and and line feed are the only two formatting attributes I have in my texts. I am trying to develop a method like this for days now without success:

func ConvertText(inputText: String) -> NSAttributedString {
    // here comes the conversion to a representation with Helvetica 14pt and Helvetica-Bold 14pt including line feeds.
}

My final goal is to display the text in an UITextView's attributedText.

Being kinda new to Swift and iOS without knowing Objective-C I found its very difficult to do String manipulations as they are quite different and complex and all examples are in Objective-C. What makes it even harder is that most API methods are not available or different in Swift than in Objective-C...

Here is what I tried so far for the method body with the help of a lot of other posts here:

var test = inputText.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!
attrStr = NSAttributedString(
        data: test,
        options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
        documentAttributes: nil,
        error: nil)!
return attrStr

The main issues here are that \n isn't converted and the font is very small and different.

Next I tried to manually bold a part of a text. It seem to work like that:

var newText = NSMutableAttributedString(string: inputText)
newText.addAttribute(NSFontAttributeName, value: UIFont(name: "Helvetica-Bold", size: 14.0)!, range: NSRange(location:2,length:4))

Now I tried to search for the attributes in the text, deleting them and use the addAttribute kinda like that

// Start of bold text
var range = newText.rangeOfString("<b>")!
// End of bold text
var range2 = newText.rangeOfString("</b>")!

// replacing attributes
newText = newText.stringByReplacingCharactersInRange(range, withString: "")
newText = newText.stringByReplacingCharactersInRange(range2, withString: "")

// creating a range for bold => error "'String.Index' is not convertible to 'int'"
// how to do such a thing
var boldRange = NSMakeRange(range.startIndex, range2.endIndex -3)

// setting bold
newText.addAttribute(NSFontAttributeName, value: UIFont(name: "Helvetica-Bold", size: 14.0)!, range: boldRange)

This whole range thing is my main issue at the moment as its quite different to a simple position in the string.

This issue is a great example for the lack of (or well hidden) documentation:

The addAttribute wants an NSRange, the rangeOfString seems to deliver a generic Range according to an error message I get - but there is no info about it. The Search Documentation button in Xcode on rangeOfString() leads to NSString. Searching in there for rangeOfString()says it returns NSRange. Clicking on that leads to the info of a type alias for _NSRange which in turn has two NSUInteger properties named location and length. Where is the startIndex and endIndex property I see in XCode? Very confusing...

Would be great if you can give me some snippets or hints where I'm wrong here or even the method body as I'm still hoping its not too difficult if you know iOS and Swift well. I'm aiming for iOS 7.1 support but if its way easier with iOS 8 only its fine as well.

1条回答
放我归山
2楼-- · 2019-03-20 07:07

Regarding your first method with NSAttributedString:

This gives (now updated for Swift 2):

func convertText(inputText: String) -> NSAttributedString {

    var html = inputText

    // Replace newline character by HTML line break
    while let range = html.rangeOfString("\n") {
        html.replaceRange(range, with: "<br />")
    }

    // Embed in a <span> for font attributes:
    html = "<span style=\"font-family: Helvetica; font-size:14pt;\">" + html + "</span>"

    let data = html.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!
    let attrStr = try? NSAttributedString(
        data: data,
        options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
        documentAttributes: nil)
    return attrStr!
}

Regarding your second method:

There are two different rangeOfString() methods, one for (Swift) String and one for (Foundation) NSString. The String method returns a Range<String.Index> and the NSString method returns an NSRange.

Converting between these two is possible but complicated. The reason is that in a String each "extended grapheme cluster" counts as one character, whereas in NSString each UTF-16 unit is counted. An extended grapheme cluster can be one or more UTF-16 unit ("

查看更多
登录 后发表回答