I have an NSTextView subclass that overrides mouseDown: to allow for image resizing. If a user clicks and drags on the edges of an image, I implement custom behaviour that resizes the image (and shows resizing cursors). If the user clicks anywhere else, super's implementation is called. This all works great.
As of macOS 27, however, the transition to gesture recognisers instead of overriding mouseDown: means that I should probably be moving away from the above approach. NSTextView now uses the new NSTextSelectionManager to implement selection and dragging via gesture recognisers, although, according to the release notes:
Existing
NSTextViewsubclasses that overridemouseDown:continue to work through a binary-compatible fallback path. (163365571)
It's unclear whether this means that we therefore should still override mouseDown: for custom behaviour in NSTextView, but to me, this, along with the content of Tech Note TN3212, strongly implies that, although it will continue to work thanks to "a binary-compatible fallback", we should entirely move away from overriding mouseDown: in the future.
If that is indeed the case, how do we implement custom dragging behaviour--such as for resizing images as in my example--in NSTextView? There still seems to be no way of doing it other than overriding mouseDown:.
I had thought that I might be able to add an NSPanGestureRecognizer to the text view and have it fail via its delegate methods if the clicks were outside of an image's edges, but a pan gesture recogniser added to an NSTextView is entirely ignored, presumably because of the private gestures already added.
Fortunately everything continues to work for now, but I would like to update my code as much as possible.
I'm really glad to hear that you're working on adopting gesture recognizers and exploring NSTextSelectionManager.
Quick clarification that I think matters here. On macOS 27 the selection interactions aren't handled by NSTextView's own event methods anymore. They're owned by NSTextSelectionManager, which installs and manages its own gesture recognizers on the text view. You can get to the manager through the new NSView.textSelectionManager property. Your pan recognizer got ignored because those selection recognizers begin right away and nothing told them to wait for yours, so yours never had a chance to start.
mouseDown: is still supported, not deprecated. What it does under the hood is worth knowing though. NSTextView sees the override and disables the NSTextSelectionManager gesture path for the whole view, falling back to the old NSEvent selection code. That's why your call-super path still behaves the way it always did. You also still need the override if you're deploying to anything older than 27, since the gesture path isn't there. The annoying part is that currently there isn't a great way to keep the override only for the older releases and still get the gesture-based selection on 27, because overriding the method is all-or-nothing per view. The best way to do this currently is providing different subclasses depending on what OS your running on, but ugh. We're still working on a better answer though so stay tuned.
How you move to gesture recognizers depends on how the image is in the view today.
If you're already vending the image through NSTextAttachmentViewProvider, you're in good shape. The attachment has its own view, so just add your resize recognizer to that view. A recognizer on a subview takes precedence over selection on its own, and selection everywhere else keeps working.
If you're drawing the image yourself or using an attachment cell, the cleanest move is to adopt NSTextAttachmentViewProvider so the image gets its own view, then do the above. That's what I'd recommend if it's at all practical for you.
If moving the image into its own view isn't an option and you need to keep drawing into the text view, you can add your own recognizer in your subclass. IMO a press gesture matches "drag on an edge" better than a pan here but you'll need to set up failure relationship yourself so selection waits on it. NSTextSelectionManager hands you its recognizers for this:
for (NSGestureRecognizer *selectionGesture in self.textSelectionManager.gesturesForFailureRequirements) {
[selectionGesture requireGestureRecognizerToFail:myEdgeResizeGesture];
}
Then have myEdgeResizeGesture begin only when the press is on an image edge (gate it in gestureRecognizerShouldBegin: or the delegate), so it fails everywhere else and selection runs as normal. Skip the failure relationship and the selection recognizers just win, which is the behavior you ran into.