Most of the daily development requirements just need to generate and display rich text, but is there a way to make rich text partially replaceable and reprocessable? Thought it over, had a flash, and wrote it down; The idea is to encapsulate the secondary encapsulation of the enumerateAttributes method, get all the child NSAttributedStrings that make up the entire rich text, and then encapsulate the dictionary with NSStringFromRange as the key

RangeSubAttStringDic, the dictionary key NSRange string is sorted as the key of each fragment, and then the corresponding subscript NSAttributedString is obtained. It is very easy to replace any part of the rich text according to the index by subscript method.

- (void)effectiveRange { NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:@"BoBiBu"]; [attString addAttribute:NSBackgroundColorAttributeName value:[UIColor systemGreenColor] range:NSMakeRange(0, 2)]; [attString addAttribute:NSFontAttributeName value:[UIFont boldSystemFontOfSize:12] range:NSMakeRange(0, 2)]; [attString addAttribute:NSFontAttributeName value:[UIFont italicSystemFontOfSize:12] range:NSMakeRange(2, 2)]; [attString addAttribute:NSBackgroundColorAttributeName value:[UIColor systemRedColor] range:NSMakeRange(2, 2)]; NSLog(@"AttributedString: %@", attString); NSDictionary<NSString *, NSAttributedString *> *dic = attString.rangeSubAttStringDic; NSArray *allKeys = [dic.allKeys sortedArrayUsingSelector:@selector(compare:)]; DDLog(@"compare_%@", allKeys); DDLog(@"dic: %@", dic); [dic enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSAttributedString * _Nonnull obj, BOOL * _Nonnull stop) { NSAttributedString *subAttString = [attString attributedSubstringFromRange:NSRangeFromString(key)]; NSLog(@"attributedSubstring: %@", subAttString); }]; } // log 2020-12-08 11:00:57.105140+0800 SwiftTemplet[71849-17013190] AttributedString: Bo{ NSBackgroundColor = "<UIDynamicSystemColor: 0x600003fbc9a0; name = systemGreenColor>"; NSFont = "<UICTFont: 0x7fc5d2431a60> font-family: \".SFUI-Semibold\"; font-weight: bold; font-style: normal; The font - size: 12.00 pt "; }Bi{ NSBackgroundColor = "<UIDynamicSystemColor: 0x600003e13620; name = systemRedColor>"; NSFont = "<UICTFont: 0x7fc5d2432750> font-family: \".SFUI-RegularItalic\"; font-weight: normal; font-style: italic; The font - size: 12.00 pt "; } Bu {} 2020-12-08 11:00:57. 105774 + 0800 SwiftTemplet [71849-17013190] - [AttrStringEffectiveController effectiveRange] [Line 98] compare_( "{0, 2}", "{2, 2}", "{4, 2} ") the 2020-12-08 11:00:57. 106189 + 0800 SwiftTemplet [71849-17013190] - [AttrStringEffectiveController effectiveRange] [Line  99] dic: { "{0, 2}" = "Bo{\n NSBackgroundColor = \"<UIDynamicSystemColor: 0x600003fbc9a0; name = systemGreenColor>\"; \n NSFont = \"<UICTFont: 0x7fc5d2431a60> font-family: \\\".SFUI-Semibold\\\"; font-weight: bold; font-style: normal; The font - size: 12.00 pt \ "; \n}"; "{2, 2}" = "Bi{\n NSBackgroundColor = \"<UIDynamicSystemColor: 0x600003e13620; name = systemRedColor>\"; \n NSFont = \"<UICTFont: 0x7fc5d2432750> font-family: \\\".SFUI-RegularItalic\\\"; font-weight: normal; font-style: italic; The font - size: 12.00 pt \ "; \n}"; "{4, 2}" = "Bu{\n}"; } 2020-12-08 11:00:57.106877+0800 SwiftTemplet[71849:17013190] attributedSubstring: Bi{ NSBackgroundColor = "<UIDynamicSystemColor: 0x600003e13620; name = systemRedColor>"; NSFont = "<UICTFont: 0x7fc5d2432750> font-family: \".SFUI-RegularItalic\"; font-weight: normal; font-style: italic; The font - size: 12.00 pt "; } 2020-12-08 11:00:57.266799+0800 SwiftTemplet[71849:17013190] attributedSubstring: Bo{ NSBackgroundColor = "<UIDynamicSystemColor: 0x600003fbc9a0; name = systemGreenColor>"; NSFont = "<UICTFont: 0x7fc5d2431a60> font-family: \".SFUI-Semibold\"; font-weight: bold; font-style: normal; The font - size: 12.00 pt "; } 2020-12-08 11:00:57.267058+0800 SwiftTemplet[71849:17013190] attributedSubstring: Bu{}Copy the code
@objc public extension NSAttributedString{/// Get all [Range: NSAttributedString] set var rangeSubAttStringDic: [String: NSAttributedString]{ get{ var dic = [String : NSAttributedString]() enumerateAttributes(in: NSMakeRange(0, self.length), options: .longestEffectiveRangeNotRequired) { (attrs, range, _) in let sub = self.attributedSubstring(from: range) dic[NSStringFromRange(range)] = sub } return dic; }} public extension NSMutableAttributedString {/ / / get or replace a certain period of NSAttributedString subscript (index: NSInteger) -> NSAttributedString? { get { let keys = rangeSubAttStringDic.keys.sorted() if index < 0 || index >= keys.count { return nil } let key = keys[index] return rangeSubAttStringDic[key] } set { guard let newValue = newValue else { return } let keys = rangeSubAttStringDic.keys.sorted() if index < 0 || index >= keys.count { return } let key = keys[index] replaceCharacters(in: NSRangeFromString(key), with: newValue) } }Copy the code

[NSAttributedString Rich text Advanced (2): chain programming implementation](www.jianshu.com/writer#/not…)