I've been struggling with this for a while now. To start off with, I am using a custom NSTextLocation
as the element range on a NSTextParagraph
. NSTextParagraph
is a subclass of NSTextElement
and both have limited public elements that can be set. From what I can see, the only thing we can set on NSTextParagraph
is an NSAttributedString
. It has a few other elements it inherits from NSTextElement
- primarily textContentManager
and elementRange
. Seems simple enough...but, there is obviously something wrong with what I am doing.
If we use NSTextStorage
as our backing store, then the text ranges it uses is a private type NSCountableTextLocation
. Being a private type, I imagine we have to implement our own NSTextLocation to use in the ranges that are set on the NSTextElement
.
But, for some reason, when I have multiple text elements, TextKit compares my custom NSTextLocation against a NSCountableTextLocation and crashes.
-[NSCountableTextLocation compare:] receiving unmatching type (3, 1)
(3,1)
here being the debug description text of my custom text location (represented by line number and column). I am at a complete loss as to why this is being triggered. I have implemented isEqual
, Comparable
and compare:
on my custom implementation.
Going with the following Text
""" First
Second
T """
I create a NSTextParagraph
for each paragraph. So, I have elements
- 'First\n' - with range (1,1) - (2,1) - length of this range is 6
- '\n' - with range (2,1) - (3,1) - length of this range is 1
- 'Second\n' with range (3,1) - (4,1) with length being 7
- '\n' - range (4,1) - (5,1) with length 1
- 'T' - range (5,1) - (5,2) length 1
This bit renders and lays out fine. If I append a character at the last location, I get the exception and crash.
""" First
Second
Th """
Exception being -[NSCountableTextLocation compare:] receiving unmatching type (3, 1)
The number of text elements and the equivalent lengths match what I would get with NSTextStorage
(executed in a different playground). There are a few subtle differences I see when debugging with lldb. When I use NSTextStorage
, the value of _attributedString
on NSTextParagraph
of the first text element (and all) is "First\n\nSecond\n\nTh" - essentially the full text. The value of attributedString
is "First\n" essentially the range of text that matches the paragraph. The generated text layout fragment has the correct line fragment "First\n" and not the full text. I don't see a public way to setup the NSTextParagraph
this way (nor am I able to do it correctly using setValue:forKey
either). I don't know if this difference really matters.
So, my questions really are
- Does
NSTextParagraph
support customNSTextLocation
? If so, how do we set it up correctly? - If not, do we subclass
NSTextElement
and do the layout ourselves?
I have a full minimal project that reproduces this at https://github.com/georgemp/TextLocationCrash but it's a bit of an involved read :-)
I have also filed reports using Feedback Assistant FB13547274
P.S. This crash only seems to occur on Sonoma (not on Monterrey or Ventura)
P.P.S - Thank you for getting this far in this lengthy read :-)