My application uses my own subclass of NSTextStorage
and everything worked fine until Mavericks. Even with the most stripped-down version, I get an exception as soon I init a text view with it. I read the documentation and also looked at How to extend NSTextStorage?. Hopefully one of you knows if Apple changed something or if I'm doing something wrong.
Info you probably need to help me:
the exception:
TextViewCheck[12542:303] : Exception *** NSRunStorage (0x6000000a4200), _replaceElements(): replaced range {0, 929} extends beyond current run storage size 928. raised during typesetting layout manager NSLayoutManager: 0x100106a80
1 containers, text backing has 928 characters selected character range {0, 0} affinity: upstream granularity: character marked character range {0, 0} Currently holding 928 glyphs.
Glyph tree contents: 928 characters, 928 glyphs, 1 nodes, 64 node bytes, 960 storage bytes, 1024 total bytes, 1.10 bytes per character, 1.10 bytes per glyph Layout tree contents: 928 characters, 928 glyphs, 0 laid glyphs, 0 laid line fragments, 1 nodes, 64 node bytes, 0 storage bytes, 64 total bytes, 0.07 bytes per character, 0.07 bytes per glyph, 0.00 laid glyphs per laid line fragment, 0.00 bytes per laid line fragment , glyph range {0 928}. Ignoring...
the backtrace
thread #1: tid = 0x137013, 0x00007fff8ad7e44e AppKit`_NSGlyphTreeGetGlyphsInRange + 1179, queue = 'com.apple.main-thread, stop reason = EXC_BAD_ACCESS (code=1, address=0x1002c0000)
frame #0: 0x00007fff8ad7e44e AppKit`_NSGlyphTreeGetGlyphsInRange + 1179
frame #1: 0x00007fff8ad7dfa1 AppKit`-[NSLayoutManager getGlyphsInRange:glyphs:characterIndexes:glyphInscriptions:elasticBits:bidiLevels:] + 92
frame #2: 0x00007fff8ad4d8b6 AppKit`-[NSATSGlyphStorage setGlyphRange:characterRange:] + 3724
frame #3: 0x00007fff8ad4c8be AppKit`-[NSATSTypesetter _ctTypesetter] + 306
frame #4: 0x00007fff8ad4baf9 AppKit`-[NSATSLineFragment layoutForStartingGlyphAtIndex:characterIndex:minPosition:maxPosition:lineFragmentRect:] + 85
frame #5: 0x00007fff8ad4a64e AppKit`-[NSATSTypesetter _layoutLineFragmentStartingWithGlyphAtIndex:characterIndex:atPoint:renderingContext:] + 2777
frame #6: 0x00007fff8ad7dc79 AppKit`-[NSATSTypesetter layoutParagraphAtPoint:] + 149
frame #7: 0x00007fff8b3f5b45 AppKit`-[NSTypesetter _layoutGlyphsInLayoutManager:startingAtGlyphIndex:maxNumberOfLineFragments:maxCharacterIndex:nextGlyphIndex:nextCharacterIndex:] + 4043
frame #8: 0x00007fff8ad7cc9d AppKit`-[NSTypesetter layoutCharactersInRange:forLayoutManager:maximumNumberOfLineFragments:] + 202
frame #9: 0x00007fff8ad7cb81 AppKit`-[NSATSTypesetter layoutCharactersInRange:forLayoutManager:maximumNumberOfLineFragments:] + 1107
frame #10: 0x00007fff8ad7b219 AppKit`-[NSLayoutManager(NSPrivate) _fillLayoutHoleForCharacterRange:desiredNumberOfLines:isSoft:] + 1306
frame #11: 0x00007fff8ae24ad9 AppKit`_NSFastFillAllLayoutHolesForGlyphRange + 1435
frame #12: 0x00007fff8ae6ddbd AppKit`-[NSLayoutManager(NSPrivate) _firstPassGlyphRangeForBoundingRect:inTextContainer:okToFillHoles:] + 309
frame #13: 0x00007fff8ae6cea4 AppKit`-[NSLayoutManager(NSPrivate) _glyphRangeForBoundingRect:inTextContainer:fast:okToFillHoles:] + 875
frame #14: 0x00007fff8add81b2 AppKit`-[NSTextView setNeedsDisplayInRect:avoidAdditionalLayout:] + 1466
frame #15: 0x00007fff8acb4222 AppKit`-[NSView setNeedsDisplay:] + 81
frame #16: 0x00007fff8add68d9 AppKit`-[NSTextView setTextContainer:] + 699
frame #17: 0x00007fff8add651a AppKit`-[NSTextContainer setTextView:] + 263
...
the code:
TTTSimpleTextStorage.m
@implementation TTTSimpleTextStorage
- (id)initWithAttributedString:(NSAttributedString *)attrStr {
if (self = [super init]) {
contents = attrStr ? [attrStr mutableCopy] :
[[NSMutableAttributedString alloc] init];
}
return self;
}
- init {
return [self initWithAttributedString:nil];
}
- (NSString *)string {
return [contents string];
}
- (NSDictionary *)attributesAtIndex:(NSUInteger)location
effectiveRange:(NSRange *)range {
return [contents attributesAtIndex:location effectiveRange:range];
}
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString
*)str {
NSInteger origLen = [self length];
[contents replaceCharactersInRange:range withString:str];
[self edited:NSTextStorageEditedCharacters range:range
changeInLength:[self length] - origLen];
}
- (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range {
[contents setAttributes:attrs range:range];
[self edited:NSTextStorageEditedCharacters range:range
changeInLength:0];
}
@end
How I use it
This is in the MainWindowController - awakeFromNib, the window IBOutlet has a NSView as subview named container and a random string string1
nts1 = [[TTTSimpleTextStorage alloc] initWithString:string1];
NSLayoutManager* lm1 = [[NSLayoutManager alloc] init];
[nts1 addLayoutManager:lm1];
NSTextContainer* tc1 = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(400, 280)];
[lm1 addTextContainer:tc1];
tv1 = [[NSTextView alloc] initWithFrame:NSMakeRect(40, 20, 400, 280) textContainer:tc1]; // here it crashes
[self.container addSubview:tv1];
[self.window makeKeyAndOrderFront:nil];
[self.window makeFirstResponder:tv1];
replaceCharactersInRange
– Brad AllredreplaceCharactersInRange
doesn't get called. onlystring
andattributesAtIndex
. – Tony J Stark