I'm trying to implement a very simple text editor that should work both with NSTextView
on macOS and UITextView
on iOS. This text editor has a toolbar button "Section Break" that inserts a new section at the current cursor position every time it is clicked. A section should be:
Visually identifiable as a section (by adding visual separators between two subsequent sections and possibly adding some vertical whitespace),
Referable. The user should be able to see a list of all sections in the editor and by clicking an item in that list, the text view should immediately scroll to the beginning of that section.
In another question I asked how to solve the first problem and unfortunately, I haven't found an answer on that part yet (even though there is a solution that works on macOS only).
However, this question focuses on the second aspect:
How can I maintain a list of all sections in my text view where each section keeps an accurate reference to the related text?
The complexity about this task is that the user can copy & paste or simply edit any part of the text at any time. Thus, I cannot keep a simple array of paragraph numbers or something like that.
What I've tried and why this appears to be such a difficult task:
One idea I've had was to subclass
NSTextStorage
and use an array of mutable attributed strings as its internal storage. I would then use a special subclass ofNSTextAttachment
and use it as a section break indicator inside my text storage. The problem with that is that the text view only calls the following methods whenever the user edits text:func replaceCharacters(in range: NSRange, with str: String)
and
func setAttributes(_ attrs: [NSAttributedString.Key : Any]?, range: NSRange)
The first method is only passed the plain string without any attributes, the second method only gets the attributes. This means that in the first method, I cannot tell if the replacement character is actually supposed to be a section break or not, so I cannot decide at that point where to create a new element in my internal text storage array.
In the second method, I don't know whether the user actually added new text (in which case I would have to add a new element in my text storage array for each section break attribute) or if the user simply changed some attributes of existing text (in which case the new array elements have already been created previously).
I've also considered the idea of using multiple text views inside a table view or stack view. However, that doesn't work because it would keep the user from selecting (and deleting) text across multiple subsequent sections.
Finally, I've tried subclassing
NSLayoutManager
, but the documentation on that is really thin and it seems to be the wrong place for me. (After all, the layout manager's responsibility is the layout of the text, not to keep track of its structure.)