Martin, Ecir, and Quincey: Thank you all! All three answers turned out to be helpful. (Quincey, you led me to learn about how to get attributed substrings, should I ever need to do it.)
Martin, your chief difference from my code seems be calling the layout manager’s ensureGlyphsForCharacterRange: in order to synch up the layout manager with the current content of text storage. I took that and ran with it. Here is the resulting code that works for me, which I donate to the public domain in case anyone else finds it useful. The methods below are found in a subclass of NSTextView: /// Set temporary foreground color for special characters in the given range -(void)setTemporaryForegroundColor:(NSColor*)color forSpecialCharacters:(NSCharacterSet*)characterSet inRange:(NSRange)range { HighlightInfo* hi = [[HighlightInfo alloc] initWithColor:color characterSet:characterSet range:range]; // If we don't delay for a half-second, Apple's Smart Quotes stop working. // I think this is a bug: why should altering temporary attributes lock out substitution? static double smartQuoteDelay = 0.5; [self performSelector:@selector(setTemporaryForegroundColor:) withObject:hi afterDelay:smartQuoteDelay]; } /// Set temporary foreground color according to information passed in HighlightInfo /// @attention Keep this private and always call via performSelector:withObject:afterDelay: -(void)setTemporaryForegroundColor:(HighlightInfo*)hi { NSLayoutManager* lm = self.layoutManager; NSTextStorage* ts = self.textStorage; NSRange searchRange = hi.range; NSUInteger beyondIx = searchRange.location + searchRange.length; // Synchronize layoutManager with textStorage: otherwise temp attribute ranges don't agree [lm ensureGlyphsForCharacterRange:searchRange]; [lm removeTemporaryAttribute:NSForegroundColorAttributeName forCharacterRange:searchRange]; // Repeatedly search and apply temporary attributes to special characters in search range NSString* str = ts.string; //NSLog( @"Searching for special chars in: %@", [str substringWithRange:searchRange]); NSCharacterSet* set = hi.characterSet; NSDictionary* tempAttrs = @{ NSForegroundColorAttributeName : hi.color }; while ( true ) { NSRange foundRange = [str rangeOfCharacterFromSet:set options:0 range:searchRange]; if ( foundRange.location == NSNotFound ) { break; } [lm setTemporaryAttributes:tempAttrs forCharacterRange:foundRange]; NSUInteger startIx = foundRange.location + foundRange.length; if ( startIx >= beyondIx ) { break; } searchRange = NSMakeRange( startIx, beyondIx - startIx ); } } The definition of HighlightInfo should be obvious, but here it is anyway: @interface HighlightInfo : NSObject @property (nonatomic) NSColor* color; @property (nonatomic) NSCharacterSet* characterSet; @property (nonatomic) NSRange range; -(instancetype)initWithColor:(NSColor*)color characterSet:(NSCharacterSet*)characterSet range:(NSRange)range; @end Cheers and happy holidays to all! — Charles Jenkins _______________________________________________ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to the list. Contact the moderators at cocoa-dev-admins(at)lists.apple.com Help/Unsubscribe/Update your Subscription: https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to arch...@mail-archive.com