I have set up a simple playground to demonstrate my problem in retrieving the attributes of attributed strings. Perhaps I do not understand how to define ranges: perhaps I am missing something else.
I have define a NSMutableAttributedString that has two colors in it as well as a change in font to bold(for the blue) and italic(for the red):
var someText = NSMutableAttributedString()
let blueAttri : [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 12), NSAttributedString.Key.foregroundColor : UIColor.blue]
let redAttri : [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: UIFont.italicSystemFont(ofSize: 12), NSAttributedString.Key.foregroundColor : UIColor.red]
someText = NSMutableAttributedString(string: "Some blue string here; ", attributes: blueAttri)
someText.append(NSMutableAttributedString(string: "Some red string here; ", attributes: redAttri))
At this point the string looks fine showing text with both colors.
I then have defined 3 ranges (attempting different approaches to defining ranges.)
var range1 = NSRange()
var range2 = NSRange(location: 0, length: someText.length)
var range3 = NSRange(location: 40, length: 44)
Lastly, I try to retrieve attributes for the text
// retrieve attributes
let attributes1 = someText.attributes(at: 0, effectiveRange: &range1)
// iterate each attribute
print("attributes1")
for attr in attributes1 {
print(attr.key, attr.value)
}
let attributes2 = someText.attributes(at: 0, effectiveRange: &range2)
// iterate each attribute
print("attributes2")
for attr in attributes2 {
print(attr.key, attr.value)
}
let attributes3 = someText.attributes(at: 0, effectiveRange: &range3)
// iterate each attribute
print("attributes3")
for attr in attributes3 {
print(attr.key, attr.value)
}
I get the following results. In all cases showing only the first set of attributes.
attributes1
NSAttributedStringKey(_rawValue: NSColor) UIExtendedSRGBColorSpace 0 0 1 1
NSAttributedStringKey(_rawValue: NSFont) font-family: ".SFUIText-Semibold"; font-weight: bold; font-style: normal; font-size: 12.00pt
attributes2
NSAttributedStringKey(_rawValue: NSColor) UIExtendedSRGBColorSpace 0 0 1 1
NSAttributedStringKey(_rawValue: NSFont) font-family: ".SFUIText-Semibold"; font-weight: bold; font-style: normal; font-size: 12.00pt
attributes3
NSAttributedStringKey(_rawValue: NSColor) UIExtendedSRGBColorSpace 0 0 1 1
NSAttributedStringKey(_rawValue: NSFont) font-family: ".SFUIText-Semibold"; font-weight: bold; font-style: normal; font-size: 12.00pt
What do I need to do to get all of the attributes in the string?
It has been suggested I use enumerate attributes. That does not seem to be legal. See below:
Using enumerateAttributes
I was able to capture ranges with Italic (just to show one example). The following is the complete code:
//Create Empty Dictionaries for storing results
var attributedFontRanges = [String: UIFont]()
var attributedColorRanges = [String: UIColor]()
//Find all attributes in the text.
someText.enumerateAttributes(in: NSRange(location: 0, length: someText.length)) { (attributes, range, stop) in
attributes.forEach { (key, value) in
switch key {
case NSAttributedString.Key.font:
attributedFontRanges[NSStringFromRange(range)] = value as? UIFont
case NSAttributedString.Key.foregroundColor:
attributedColorRanges[NSStringFromRange(range)] = value as? UIColor
default:
assert(key == NSAttributedString.Key.paragraphStyle, "Unknown attribute found in the attributed string")
}
}
}
//Determine key(range) of Italic font
var italicRange = attributedFontRanges.filter { $0.value.fontName == ".SFUIText-Italic" }.keys
print("italicRange: \(italicRange)")
The following results are printed: italicRange: ["{23, 22}"]
You are using attributes
with the same at:
value of 0
for all three calls. That returns the attributes for the 1st character in the string.
If you want all of the attributes in a range, use enumerateAttributes
and pass in the range you want to iterate over.
Note: The range you pass needs to be based on the UTF-16 encoding of the string, not the Swift string length. Those two lengths can be different when you have special characters such as Emojis. Using NSAttributedString length
is fine.
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