问题
I'm working on a rich text editor for iOS, using a UITextView
along with an NSAttributedString
. It'll function similarly to traditional ones (i.e. selecting a region, clicking a button, and it applies that effect to that region, while preserving any other attributes on the text.
Unfortunately with NSAttributedString
, not all of them can be adjusted independently. Several of them (at least bold, font face, and font size) all require passing a UIFont
, which'll set all of those attributes for the region (even if you only wanted to set one). Seeing as a single region might include several faces and sizes, this'll result in the naive approach breaking a lot of existing formatting.
Is there any recommended means to accomplish this? I'm thinking my only option will be to iterate through the attributes for the region I want to apply it to, group it into chunks that have the same other font attributes, and then apply it to each chunk individually.
回答1:
You have the right idea and the algorithm is already provided for you by enumerateAttribute:inRange:options:usingBlock:
. To use this to process font runs:
[myAttributedString enumerateAttribute:NSFontAttributeName
inRange:selectedRange
options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
usingBlock:addBoldBlock
]
where addBoldBlock takes the current font (supplied as first argument to the block), adds bold, and applies it to the current chunk (supplied as second argument to the block).
回答2:
This is what I use in my DTRichTextEditor:
- (void)toggleBoldInRange:(NSRange)range
{
// first character determines current boldness
NSDictionary *currentAttributes = [self typingAttributesForRange:range];
if (!currentAttributes)
{
return;
}
[self beginEditing];
CTFontRef currentFont = (__bridge CTFontRef)[currentAttributes objectForKey:(id)kCTFontAttributeName];
DTCoreTextFontDescriptor *typingFontDescriptor = [DTCoreTextFontDescriptor fontDescriptorForCTFont:currentFont];
// need to replace name with family
CFStringRef family = CTFontCopyFamilyName(currentFont);
typingFontDescriptor.fontFamily = (__bridge NSString *)family;
CFRelease(family);
typingFontDescriptor.fontName = nil;
NSRange attrRange;
NSUInteger index=range.location;
while (index < NSMaxRange(range))
{
NSMutableDictionary *attrs = [[self attributesAtIndex:index effectiveRange:&attrRange] mutableCopy];
CTFontRef currentFont = (__bridge CTFontRef)[attrs objectForKey:(id)kCTFontAttributeName];
if (currentFont)
{
DTCoreTextFontDescriptor *desc = [DTCoreTextFontDescriptor fontDescriptorForCTFont:currentFont];
// need to replace name with family
CFStringRef family = CTFontCopyFamilyName(currentFont);
desc.fontFamily = (__bridge NSString *)family;
CFRelease(family);
desc.fontName = nil;
desc.boldTrait = !typingFontDescriptor.boldTrait;
CTFontRef newFont = [desc newMatchingFont];
[attrs setObject:(__bridge id)newFont forKey:(id)kCTFontAttributeName];
CFRelease(newFont);
if (attrRange.location < range.location)
{
attrRange.length -= (range.location - attrRange.location);
attrRange.location = range.location;
}
if (NSMaxRange(attrRange)>NSMaxRange(range))
{
attrRange.length = NSMaxRange(range) - attrRange.location;
}
[self setAttributes:attrs range:attrRange];
}
index += attrRange.length;
}
[self endEditing];
}
This uses DTCoreTextFontDescriptor from the open source DTCoreText project.
PS: You'd have to adjust it for use with UIFont accordingly.
来源:https://stackoverflow.com/questions/14161254/apply-bolding-to-portion-of-nsattributedstring-while-preserving-font-sizes-and