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.
let label = UILabel() label. attributedText = NSMutableAttributedString() . bold("Address: ") .
Overview. Attributed strings are character strings that have attributes for individual characters or ranges of characters. Attributes provide traits like visual styles for display, accessibility for guided access, and hyperlink data for linking between data sources.
Regarding your first method with NSAttributedString
:
\n
character in HTML is just ordinary white space. To get a line break you
would have to replace it by <br />
first.<span>
, see Parsing HTML into NSAttributedText - how to set font?.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 ("😄" is two UTF-16 units, "🇩🇪" is four).
The addAttribute()
method accepts only an NSRange
. The easiest method to solve
this problem is to convert the Swift string to NSString
and work with NSRange
only. Then your method could look like this:
func convertText(inputText: String) -> NSAttributedString {
let attrString = NSMutableAttributedString(string: inputText)
let boldFont = UIFont(name: "Helvetica-Bold", size: 14.0)!
var r1 = (attrString.string as NSString).rangeOfString("<b>")
while r1.location != NSNotFound {
let r2 = (attrString.string as NSString).rangeOfString("</b>")
if r2.location != NSNotFound && r2.location > r1.location {
let r3 = NSMakeRange(r1.location + r1.length, r2.location - r1.location - r1.length)
attrString.addAttribute(NSFontAttributeName, value: boldFont, range: r3)
attrString.replaceCharactersInRange(r2, withString: "")
attrString.replaceCharactersInRange(r1, withString: "")
} else {
break
}
r1 = (attrString.string as NSString).rangeOfString("<b>")
}
return attrString
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With