Reputation: 1225
I want to convert NSAttributedString containing RTFD to uppercase without losing attributes of existing characters and graphics.
Thanks,
Upvotes: 11
Views: 8313
Reputation: 21
When you uppercase German "ß" character, it becomes "SS"; therefore the range changes. This might cause crash in the accepted answer. Here's my proposal to cover this case as well:
extension NSAttributedString {
func uppercased() -> NSAttributedString {
// Original NSAttributedString
let originalAttributedString = self
// Convert the attributed string to a regular string
let originalString = originalAttributedString.string
// Uppercase the string
let uppercasedString = originalString.uppercased()
// Create a new mutable attributed string with the uppercased string
let uppercasedAttributedString = NSMutableAttributedString(string: uppercasedString)
// Track the difference in length due to character count changes
var indexOffset = 0
originalAttributedString.enumerateAttributes(in: NSRange(location: 0, length: originalAttributedString.length),
options: []) { attributes, range, _ in
// Extract the substring from the original string for this range
let originalSubstring = originalAttributedString.attributedSubstring(from: range).string
// Uppercase the substring
let uppercasedSubstring = originalSubstring.uppercased()
// Calculate the difference in length between the original and uppercased substrings
let lengthDifference = uppercasedSubstring.count - originalSubstring.count
// Adjust the range for the uppercased string (apply indexOffset before adjustment)
let adjustedRange = NSRange(location: range.location + indexOffset, length: uppercasedSubstring.count)
// Apply attributes to the new range in the uppercased attributed string
uppercasedAttributedString.addAttributes(attributes, range: adjustedRange)
// Update the index offset for future ranges
indexOffset += lengthDifference
}
return uppercasedAttributedString
}
}
Upvotes: 0
Reputation: 2915
EDIT:
@fluidsonic Is correct that the original code is incorrect. Below is an updated version in Swift, that replaces the text in each attribute range with an uppercased version of the string in that range.
extension NSAttributedString {
func uppercased() -> NSAttributedString {
let result = NSMutableAttributedString(attributedString: self)
result.enumerateAttributes(in: NSRange(location: 0, length: length), options: []) {_, range, _ in
result.replaceCharacters(in: range, with: (string as NSString).substring(with: range).uppercased())
}
return result
}
}
Original answer:
- (NSAttributedString *)upperCaseAttributedStringFromAttributedString:(NSAttributedString *)inAttrString {
// Make a mutable copy of your input string
NSMutableAttributedString *attrString = [inAttrString mutableCopy];
// Make an array to save the attributes in
NSMutableArray *attributes = [NSMutableArray array];
// Add each set of attributes to the array in a dictionary containing the attributes and range
[attrString enumerateAttributesInRange:NSMakeRange(0, [attrString length]) options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
[attributes addObject:@{@"attrs":attrs, @"range":[NSValue valueWithRange:range]}];
}];
// Make a plain uppercase string
NSString *string = [[attrString string]uppercaseString];
// Replace the characters with the uppercase ones
[attrString replaceCharactersInRange:NSMakeRange(0, [attrString length]) withString:string];
// Reapply each attribute
for (NSDictionary *attribute in attributes) {
[attrString setAttributes:attribute[@"attrs"] range:[attribute[@"range"] rangeValue]];
}
return attrString;
}
What this does:
NSString
method.Upvotes: 13