diff --git a/packages/react-native-enriched-markdown/ios/renderer/ListItemRenderer.m b/packages/react-native-enriched-markdown/ios/renderer/ListItemRenderer.m index 018014b63..cc554b2f6 100644 --- a/packages/react-native-enriched-markdown/ios/renderer/ListItemRenderer.m +++ b/packages/react-native-enriched-markdown/ios/renderer/ListItemRenderer.m @@ -19,6 +19,41 @@ @implementation ENRMListMarkerDescriptor @end +// NSArray's -hash is its count, so plain one-marker arrays all collide in the +// table that uniques attribute dictionaries, and each list item probes past +// every earlier one. Hashing by the item's own marker keeps rendering linear. +@interface ENRMListMarkerArray : NSArray +@end + +@implementation ENRMListMarkerArray { + NSArray *_markers; +} + +- (instancetype)initWithMarkers:(NSArray *)markers +{ + if (self = [super init]) { + _markers = [markers copy]; + } + return self; +} + +- (NSUInteger)count +{ + return _markers.count; +} + +- (id)objectAtIndex:(NSUInteger)index +{ + return _markers[index]; +} + +- (NSUInteger)hash +{ + return _markers.lastObject.hash; +} + +@end + @interface ListItemRenderer () - (void)applyCheckedDecorationsTo:(NSMutableAttributedString *)output range:(NSRange)range @@ -184,7 +219,9 @@ - (void)renderNodeContent:(MarkdownASTNode *)node NSArray *existingMarkers = [output attribute:ListItemMarkerStartAttribute atIndex:anchorLocation effectiveRange:NULL]; NSArray *markers = [existingMarkers isKindOfClass:[NSArray class]] ? [existingMarkers arrayByAddingObject:marker] : @[ marker ]; - [output addAttribute:ListItemMarkerStartAttribute value:markers range:anchorRange]; + [output addAttribute:ListItemMarkerStartAttribute + value:[[ENRMListMarkerArray alloc] initWithMarkers:markers] + range:anchorRange]; if (isTask && isChecked) { [self applyCheckedDecorationsTo:output range:itemRange nestingLevel:nestingLevel];