Opting Out of TextKit 2 in NSTextView

Starting in macOS 13 Ventura, Apple is automatically opting-in most NSTextView instances to use TextKit 2, a modern replacement for the traditional NSLayoutManager based text system that has been part of Mac OS X since its debut.

This change may bring a variety of surprises, so it’s important to test carefully if you use NSTextView. Somewhat unintuitively, you are more likely to be affected by the changes if you do little customization of the default NSTextView behavior.

Apple explained the new opt-in behavior and some of the consequences in the 2022 WWDC session titled “What’s New in TextKit and text views”. They also explain that the decision to use TextKit 2 can be explicitly opted out of:

When you explicitly call an NSLayoutManager API, the text view replaces its NSTextLayoutManager with an NSLayoutManager and reconfigures itself to use TextKit 1.

Perhaps the biggest risk for malfunction lies in scenarios where some significant customization is expected to work, but not in a way that affects the text view’s decision to carry on using TextKit 2. One such example is when it comes to customizing the drawing of the insertion point cursor. Since “the dawn of time” Apple has offered an overridable method on NSTextView:

open func drawInsertionPoint(in rect: NSRect, color: NSColor, turnedOn flag: Bool)

A custom NSTextView subclass that thinks it can do better than Apple’s default text-colored vertical bar can override the method to impose its own dubious design choices:

Screenshot of text editor text with an insertion point colored red and green

By default, starting in macOS 13 Ventura, the above customization will fail, because it is evidently not supported by TextKit 2. The simple workaround, for the time being anyway, is to force your text view to use TextKit 1. As explained in the WWDC excerpt above, this is as simple as asking it once for its layout manager, which will cause it to rebuild its entire text architecture to suit the TextKit 1 way of functioning:

let _ = myTextView.layoutManager

I’ve filed FB11771261 with Apple, requesting that the functionality either be restored, or the method in question be overtly documented as non-functioning.

Update: The insertion point behavior described here is among many other bugs documented on Marcin Krzyzanowski’s STTextView project on GitHub. Looks like a good resource for folks who are curious about possible issues, and possibly a good alternative to NSTextView until/unless Apple fixes NSTextView to be more functional in TextKit 2 mode. Thanks to Greg Pierce for the link.