Setting a custom tab order in a Cocoa application - cocoa

I have a window with two columns of fields. On the left, there is an NSTableView and an NSTokenField, and on the right, there are two NSTextFields. I want the tab order to go down the left, then down the right. (So the order should be NSTableView, NSTokenField, NSTextField, NSTextField in my window.) However, Cocoa appears to be determining its own preferred order, going from the top to the bottom. The NSTokenField is positioned lower in the window than any other control, so it will always tab from NSTableView, to the right NSTextFields, then back to the bottom left NSTokenField.
I have tried following this section of the Apple developer documentation called Enable Tabbing Between Text Fields and dragging nextKeyView in Interface Builder between the fields in the order that I want. This seems to have absolutely zero effect on the tab order, and from what I can tell, Cocoa appears to still use its default detecting method to choose a tab order.
Any ideas? My target is 10.6+.

Make sure that you also set the initialFirstResponder outlet of the window to the first field (the table view in this case).

Sounds like you're going to have to do it programmatically:
Register for controlTextDidEndEditing notifications, identify the field by tag, and then call makeFirstResponder:fieldOfYourChoice on the window. And/or use an IBAction on the field, identifying it by sender, and call makeFirstResponder.

Related

How to make a NSToolbar item toggle state

I have an NSToolbar with NSToolbarItem instances. One of the toolbar buttons is in one of two modes, depending on whether it currently operating (has been clicked) or not. I am handling this in code by changing the icon for the button to have a background rectangle when the command it represents is operational, but I can't help thinking there must be another way.
I've tried using the Selectable checkbox in XCode Interface Builder attribute inspector, and it sort of gives the result I want, except when it is selected I can't click any of the other toolbar items. I also can't see how to deselect it.
I'm a bit of a Cocoa noob so I expect the two state toggle thing is just waiting for me to find it, except so far I haven't been able to.
This seems like it would be a common thing to want to do, thing is how?

Tabbing between NSTextFields with nextKeyView

I have a single NSViewController with the following layout, set using a storyboard:
The nextKeyView outlet of each of the NSTextFields is configured to be the next NSTextField in the order presented on the screenshot. For example, I chose the server NSTextField in IB and dragged from the nextKeyView outlet in Connectivity inspector to the login NSTextField, and did the same for the rest of the fields.
When the app is launched, any tab press on any of the field moves the selection to the first NSTextField. How do I achieve the desired tabbing between the fields?
I tried this in the respective WindowController, but to no avail:
- (void)windowDidLoad {
[super windowDidLoad];
self.window.initialFirstResponder = self.serverTextField;
}
This seems to be the most detailed answer, from Justin Bur posted to cocoa-dev mailing list (31 Jan 2007).
On several occasions over the years, people have asked why their key
view loop doesn't work properly. Most of these queries never get
answered on the list. After failing to find help for my key view loop
problems either on this list or on web sites, I did some
experimenting.
The key view loop can be problematic to deal with. It is designed to
just work magically, so in most cases it's not an issue. But if it
doesn't work, it's pretty difficult to figure out why not. Here are
some guidelines for getting a working key view loop.
Consider whether you can settle for an automatically generated key view loop. Each responder's top left corner determines its placement
in the loop. The loop proceeds from upper left to lower right, row by
row (at least for left-to-right scripts). This is by far the easiest
solution. To enable this, make sure the window's initialFirstResponder
is nil. See also -[NSWindow recalculateKeyViewLoop].
If the automatic key view loop is not suitable, set up your own key view loop using Interface Builder as much as possible. The window's
initialFirstResponder outlet must be set, in order to disable
automatic key loop generation. From that responder around the loop,
set the nextKeyView outlet of each item in the loop. (If desired, the
last item's nextKeyView can be set to the first item, thus closing the
loop.) For any view with scrollbars (NSTextView, NSTableView, etc.),
you should use the enclosing NSScrollView when setting nextKeyView.
If you have any responders created in code, splice them into the key view loop early (preferably in awakeFromNib
or maybe -[NSWindowController windowDidLoad]).
For each (sequence of) new item(s), you must use call -[NSView setNextKeyView:] thus: once to make
the previous item point to the (first) new one, (calls to make each
new item point to the next), and finally to make the (last) new item
point to its successor.
If your window has a toolbar, toolbar items that are interested in becoming key view will automatically add and remove themselves as the
toolbar is shown or hidden. The toolbar does not take into account the
return value of -[NSWindow autorecalculatesKeyViewLoop]. Toolbar items
are always placed in the loop before the top leftmost item. There is
no easy way to change this.
Once the window has been displayed, it can be extremely difficult to modify the key view loop - in particular if you are using
NSScrollView or NSTabView. These (and others?) are special cases
because they insert their contained views into the loop automatically.
For information on the initialFirstResponder and key view loop of an
NSTabViewItem, see the AppKit release notes for OS X 10.1
.
If you have items that should sometimes be in the loop and other times not, it is not advisable to attempt to splice them in and out of
the loop. Instead, subclass -[NSResponder acceptsFirstResponder] for
these items. If an item returns NO from this method, it will be left
out of the loop (temporarily); if it returns YES, it will come back
into the loop. Alternately, if the item is derived from NSControl (it
probably is), you can call setRefusesFirstResponder: on it.
If you make a mistake, your key view loop will cease to function, either in one direction or in both. Once it breaks it stays broken. To
debug, comment out calls to setNextKeyView: or
setInitialFirstResponder: until it works again. The offending call is
likely trying to modify the key view loop in the presence of
NSScrollView or NSTabView, after these objects have already done their
behind-the-scenes loop-munging. Move the calls to an earlier point, or
do without. (If you have no calls to setNextKeyView:, then check your
nib - make sure the window's initialFirstResponder is set and that
nextKeyView outlets are chained together the way you want.)
In System Preferences/Keyboard & Mouse/Keyboard Shortcuts, at the bottom of the pane under "Full keyboard access", you can control
whether key view loops include all controls or only text fields and
scrolling lists (^F7 to toggle). You should test your key view loops
with this setting in each state.
These guidelines were determined by experiment and may not be entirely
accurate. Corrections and further explanations are most welcome.
Set the window's initialFirstResponder in windowDidLoad of the window controller or viewWillAppear of the view controller. If initialFirstResponder isn't set before the window's makeKeyAndOrderFront, recalculateKeyViewLoop is called.

Cursor blinking when NSTextView is not the first responder?

I have an NSTextView object within a complex custom view. I need that custom view to be the first responder (for multiple reasons, e.g. to have its focus ring drawn properly). But the NSTextView cursor must still blink as if the text view was the first responder.
Here's essentially what I want to see in my application (a screenshot of the search field in Apple Mail):
Note that the text view is the empty area with the cursor located at the beginning of it. The other elements belong to the parent custom view, which has the focus ring around it. The cursor is blinking!
I couldn't implement that behavior using the standard controls. My final solution is to put the search field in another custom view with broad margins and implement a custom focus ring drawing.
Here's how my drafty control looks like:

NSTextField with automatic NSNumberFormatter in Interface Builder

I've been making iOS apps for awhile, but I'm trying my hand at MacOS development. I'm adding an NSTextField to my UI and I noticed in Xcode that one of the options in the graphical widgets is "NSTextField with NSNumberFormatter" which implies to me that I'll be able to restrict the input of the field to numbers and configure the formatter in some way.
When I add the NSTextField with NSNumberFormatter to my UI, I can see it has a formatter outlet which appears to be kind of linked to an NSNumberFormatter (although the name is a little grayed out). However, I can't figure out any way to interact with or configure that NSNumberFormatter.
Any help?
To access the NSNumberFormatter, you have to select it in the dock (that list of objects on the left side of the XCode 4 Interface Builder [IB] window).
If the dock isn't in outline view, e.g., it just shows about 4 icons, click the triangle-in-a-square-button at the bottom of the dock. The dock should now show a "Placeholders" section and an "Objects" section; the objects are your UI objects in a hierarchical outline view.
In the IB window, click your NSTextField; that'll highlight the corresponding Text Field Cell in the outline (you may have to twiddle down some disclosure triangles to see it). The Text Field Cell should have a disclosure triangle; twiddle it down to reveal the Number Formatter. Select it, and you should now be able to manipulate it in the Inspector panel.
(There are a lot of things non-obvious like that in XCode. When in doubt, examine your UI object in the Dock's outline view, or prowl the menus with that object selected. It's amazing--and often useful--what you can discover lurking there!
to configure the number formatter, you can ( after you've selected the formatter ) open the Attributes inspector, select the behavior you want and customize the formatter. At least that worked for me in XCode 4.
– moritz

Validating a drag to an NSCollectionView isn't reflected visually

I have an NSCollectionView that I want to accept items dragged from elsewhere in my application.
I implement collectionView:validateDrop:proposedIndex:dropOperation: and collectionView:acceptDrop:index:dropOperation: in the collectionview's delegate and register for the appropriate dragged types. Both methods get called fine when I drag the appropriate types, but I don't get a blue focus ring over the collectionview indicating a valid drag.
Have tried both the collection view and its containing scroll view on Default and External settings for the focus ring. Both are just the standard non-derived Cocoa classes. Wondered if there was anything else I should try. Surely it isn't necessary to subclass NSCollectionView for this?
Thanks
Chris
Focus rings are not typically the correct way to provide feedback about drag destinations. Every view does it slightly differently. NSTextView shows the insertion bar. NSTableView shows a blue line in between rows for Before drop operations, and shows a bezel around the row for On drop operations. (See NSTableViewDropOperation)
NSCollectionView shows a "gap" between existing subviews to show where the items will be dropped for Before drop operations, and it will set the selected property on NSCollectionViewItem to YES for On drop operations. (Note: NSCollectionViewItem doesn't do anything by default to visibly represent the selected property. You must implement that yourself.)
Since NSCollectionView's feedback uses existing subviews only, it appears there isn't any feedback at all for empty NSCollectionView's. You would need to subclass to provide this behavior yourself. You could also file a bug to request that NSCollectionView do this itself.

Resources