The
NSTextViewportLayoutControllerDelegate.textViewportLayoutControllerDidLayout(_:)
documentation states that
Layout information on textViewportLayoutController is up-to-date at the point of this call.
however it is easy to put the NSTextViewportLayoutController in a state where after calling textViewportLayoutControllerDidLayout, the value of viewportRange
is nil (unexpected) and value of the property viewportBounds
is .zero
The TextKit2 sample application found at https://developer.apple.com/documentation/uikit/using-textkit-2-to-interact-with-text makes that assumption as well, and in few places force unwrap the value of viewportRange
, that leads to runtime crashes.
This behavior is also discussed in Developer Forum thread about TextKit2 viewport relocation: https://developer.apple.com/forums/thread/761364?answerId=800516022#800516022
How to reproduce:
- Run Mac target of LayoutWithTextKit2 sample project found at https://developer.apple.com/documentation/uikit/using-textkit-2-to-interact-with-text
- locate menu.rtf file and duplicate its content several times - the goal is to increase the length of the layout text
- quickly resize application window - that results in viewport layouts - that result in out-of-bound viewport - that results ina crash
- OR quickly scroll down/up to the end of the document using scroller bar on the right side of the window
Reproducible 100%
The situation occurs when the document is not fully laid out, the estimated size (height) of the content exceeds the final (correct) height, and the layoutViewport() function is executed quickly. Resulting in partial viewport layout, and once the viewport moves outside of the document's total height, the viewportLayoutController starts to report viewportRange = nil
.
FB19698121
Why does it happen? Is it expected? How to recover from that state? And most importantly, how to make the NSTextLayoutManager display the portion of the document that is currently scrolled to. and how to do it without for ce layout the full document on each viewportLayout()