How to do "Standard" macOS text field editing?

It seems like every single app does this, and it's so ubiquitous, I can't believe there isn't a simple, straight-forward "right" way... so I'm going to assume I'm just being an ***** and can't find it.


Think of Finder (or Xcode,or Sketch, or any number of apps). You have an NSOutlineView, or NSTableView, or whatever... a list of resources in some fashion. You click on a resource (presumably represented by an NSTextField) to select it, then you either click it again, double-click it, or press Enter, and the text field becomes editable, you change the name of the resource, then hit enter, or click away, and it switches back to display mode, and the controller saves the change.


Can anyone point me to the API or a Guide on how to implement this pattern? As I said, it's so common, I find it hard to believe it's not a realtively straight-forward thing to do in Cocoa, but I just cannot seem to find the correct implementation.

Replies

The reason it's so ubiquitous is that it's largely automatic. So, what have you tried? In what context are you trying to get this to happen? (I.e. are you in fact using a table view or outline view?) What were the actual results of your attempt?

Thanks for the reply.


In my current project, I'm using an NSCollectionView (flow layout) (I've found NSOutlineView to not be very Swift friendly). Each NSCollectionViewItem represents an aspect of the current project, and contains a mildly complex set of UI elements, one of which is the NSTextField which contains the aspect name. So what do I need to do to that NSTextField (ie: properties needing set, delegate methods to implement, IB connections, etc) and its parent(s) to make this work?

Hmm... so I just found "Behavior: Editable" in IB... somehow I never saw that before... Oddly, it works for some of my views, but on others, setting that to Editable, makes the whole NSTextField simply not appear 😕... Maybe a beta bug? I've been seeing some weird stuff relating to layers and controls rendering.

If you're using auto layout and relying on the text field to have an instrinsic width to get the layout right, that may be your problem. I think that an editable text field doesn't have an instrinsic width because its content is "extrinsic". Add an explicit width constraint or make sure that the text field is constrained at both leadng and trialing (or either and centered) so that it is "pulled" wide enough.

Ah, thanks, that may be the issue. For now, I'm going to try to get one working fully, then worry about the edge cases.


So, this is sort-of working, but this isn't exactly the behavior that I'm after... maybe there's another peice?


By setting "Behavior: Editable" in IB (I'm assuming this is equivalent to field.isEditable), any click on the field places the focus-ring and the cursor, so basically its an editable text field that doesn't look editable. Whereas the standard behavior seems to be that a click on the field acts as a selection of the resource, then a second click, or pressing Enter begins editing, changes the bezel/styling so the field looks editable, and sets the selection to the whole of the text.


So it seems there are some events that need to be handled and state to be managed... Are these my responsibility or is there another property that will cause this behavior? And if it's mine to manage, what is the best-practice for this?

Check out -[NSResponder validateProposedFirstResponder:forEvent:]. See Apple's Table View Programming Guide to learn how it's used in view-based table views. There's also a blog post about it by the Apple dev who wrote the view-based table view implementation, although it's about how to defeat the click-twice mechanism that you're trying to implement. It does help with understanding, though.


Assuming NSCollectionView doesn't already have support for this, you'll presumably want to write a custom subclass that overrides it to implement logic like that described for table views.

Could really use some "official" help here... any pointers to API docs or Guides welcome! 🙂