NSTextField usesSingleLineMode Stops Working When You Implement NSTextViewDelegate Methods

Today I learned why my NSTextField permits pasting of newline characters even though I set usesSingleLineMode properly. It's because I made it conform to NSTextViewDelegate to cache changes. When you edit text inside of an NSTextField, you actually type inside a field editor of the window. That's a shared NSTextView instance. Most of the hard work of an NSTextField is done by its cell, which is an NSTextCell. NSTextCells implement at least the delegate method NSTextViewDelegate.textView(_:shouldChangeTextIn:replacementText:) – and when you set usesSingleLineMode, this is actually set for the cell, not the view itself. You can use textView(_:shouldChangeTextIn:replacementText:) to sanitize input text, and I suspect that's where the usesSingleLineMode implementation happens. If your NSTextField subclass implements this method, the NSTextCell implementation isn't called. And since that one isn't public (it was called "implicit protocol conformance" back in the day), you cannot delegate up in Swift because the compiler knows it isn't there.

Continue reading …

RxSwift: Typing/Idle Event Stream for NSTextView

To improve my note editing app The Archive's responsiveness, I want to find out when the user is actively editing a note. Here's how to create the idle switch in RxSwift/RxCocoa. A simple enum of .typing and .idle will do for a start. Of course, NSTextViewDelegate provides a textDidChange(_:) callback to hook into; it's based on a notification, so you can also subscribe to the NSText.didChangeNotification directly if you don't want to use a delegate. That's the input signal I'm going to use.

Continue reading …

NSTextView's Default Insertion Point and Selected Text Colors

Teaser image

NSTextView can be customized to display different colors for the insertion point (aka caret, aka text cursor) and for selected text. This is especially useful when you add themes to your editor and the default settings don't fit anymore. The default values are not exposed anywhere, so I had to log them and reconstruct convenient accessors to reset a text view if needed:

Continue reading …

NSTextView: When Did the Find Bar Disappear?

For whatever reason, my current app project's find bar does not make the text view firstResponder again when you hit Escape or click the "Done" button to close it. This is very uncomfortable for users: they type away, hit ⌘F to find a phrase, then hit Esc – and now they're in limbo. To my astonishment, the NSTextFinderAction called hideFindInterface is not triggered when you make the find bar disappear. Its opposite, showFindInterface, is triggered when the find bar slides back in, though. Intercepting in NSTextView.performTextFinderAction(_:) does not help, then.

Continue reading …