问题
I have a master-detail kind of application OS X 10.9 only:
An NSTableView on the left side of the window, the details as NSTextFields on the right side.
I have not set the tab order specifically, out of the box works just fine.
Except after inserting a new object:
- after inserting a new object the object is selected in the NSTableView.
- hit tab
- the first NSTextField is selected
- write something, hit tab
- now instead of selecting the next NSTextField nothing is selected,
- hit tab
- the NSTableView is selected
- hit tab
- the first NSTextField is selected
What i want:
- after inserting a new object, the first NSTextField of the detail part should be selected
- hit tab
- the next NSTextField of the detail part should be selected
- …
UPDATE: I found that the strange behaviour is triggered by enabling NSArrayController’s "Auto Rearrange Content“ in Interface Builder. If i disable it, i do not loose my focus anymore. See CoreData-bound NSTableView loses input focus when items change, but only if sorted.
BUT, of course, i still want my content to be automatically sortet — just without loosing my focus during that operation. Can this be done?
UPDATE2: i followed @KenThomases advice and recorded the stack trace at the moment, when my textfield looses keyboard focus:
2014-08-17 16:30:26.003 ResponderExperiment[712:303] (
0 ResponderExperiment 0x00000001000014dd -[WMWindow makeFirstResponder:] + 61
1 AppKit 0x00007fff8d25c778 -[NSTextView(NSPrivate) _giveUpFirstResponder:] + 257
2 AppKit 0x00007fff8d25c56c -[NSTextView(NSKeyBindingCommands) insertTab:] + 270
3 AppKit 0x00007fff8d22dc2f -[NSResponder doCommandBySelector:] + 71
4 AppKit 0x00007fff8d25b10a -[NSTextView doCommandBySelector:] + 196
5 AppKit 0x00007fff8d22d151 -[NSKeyBindingManager(NSKeyBindingManager_MultiClients) interpretEventAsCommand:forClient:] + 1392
6 AppKit 0x00007fff8d24c1c2 -[NSTextInputContext handleEvent:] + 845
7 AppKit 0x00007fff8d22b9dd -[NSView interpretKeyEvents:] + 180
8 AppKit 0x00007fff8d24bd6d -[NSTextView keyDown:] + 658
9 AppKit 0x00007fff8d1f856b -[NSWindow sendEvent:] + 1843
10 AppKit 0x00007fff8d199b32 -[NSApplication sendEvent:] + 3395
11 AppKit 0x00007fff8cfe99f9 -[NSApplication run] + 646
12 AppKit 0x00007fff8cfd4783 NSApplicationMain + 940
13 ResponderExperiment 0x0000000100002de2 main + 34
14 libdyld.dylib 0x00007fff8c5bf5fd start + 1
15 ??? 0x0000000000000003 0x0 + 3
)
2014-08-17 16:30:26.014 ResponderExperiment[712:303] (
0 ResponderExperiment 0x00000001000014dd -[WMWindow makeFirstResponder:] + 61
1 AppKit 0x00007fff8d03c918 -[NSControl abortEditing] + 83
2 AppKit 0x00007fff8d2723e0 -[NSValueBinder discardEditing] + 162
3 AppKit 0x00007fff8d1aee30 -[NSController discardEditing] + 115
4 AppKit 0x00007fff8d1af971 -[NSArrayController rearrangeObjects] + 27
5 AppKit 0x00007fff8d3697a9 -[NSArrayController observeValueForKeyPath:ofObject:change:context:] + 294
6 AppKit 0x00007fff8d39ac08 -[NSArrayController _setMultipleValue:forKeyPath:atIndex:] + 323
7 AppKit 0x00007fff8d39ba12 -[NSArrayController _setSingleValue:forKeyPath:] + 137
8 Foundation 0x00007fff8bf9e19a -[NSObject(NSKeyValueCoding) setValue:forKeyPath:] + 285
9 AppKit 0x00007fff8d25444c -[NSBinder _setValue:forKeyPath:ofObject:mode:validateImmediately:raisesForNotApplicableKeys:error:] + 364
10 AppKit 0x00007fff8d254287 -[NSBinder setValue:forBinding:error:] + 245
11 AppKit 0x00007fff8d74c4ee -[NSValueBinder _applyObjectValue:forBinding:canRecoverFromErrors:handleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:] + 194
12 AppKit 0x00007fff8d74c871 -[NSValueBinder applyDisplayedValueHandleErrors:typeOfAlert:canRecoverFromErrors:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:error:] + 621
13 AppKit 0x00007fff8d74c9d4 -[NSValueBinder _applyDisplayedValueIfHasUncommittedChangesWithHandleErrors:typeOfAlert:discardEditingCallback:otherCallback:callbackContextInfo:didRunAlert:error:] + 127
14 AppKit 0x00007fff8d253c2e -[NSValueBinder validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:] + 436
15 AppKit 0x00007fff8d253a57 -[_NSBindingAdaptor _validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:bindingAdaptor:] + 160
16 AppKit 0x00007fff8d25399d -[_NSBindingAdaptor validateAndCommitValueInEditor:editingIsEnding:errorUserInterfaceHandled:] + 260
17 AppKit 0x00007fff8d25e0ba -[NSTextField textShouldEndEditing:] + 402
18 AppKit 0x00007fff8d25dc63 -[NSTextView(NSSharing) resignFirstResponder] + 393
19 AppKit 0x00007fff8d136170 -[NSWindow makeFirstResponder:] + 455
20 ResponderExperiment 0x000000010000154c -[WMWindow makeFirstResponder:] + 172
21 AppKit 0x00007fff8d25c778 -[NSTextView(NSPrivate) _giveUpFirstResponder:] + 257
22 AppKit 0x00007fff8d25c56c -[NSTextView(NSKeyBindingCommands) insertTab:] + 270
23 AppKit 0x00007fff8d22dc2f -[NSResponder doCommandBySelector:] + 71
24 AppKit 0x00007fff8d25b10a -[NSTextView doCommandBySelector:] + 196
25 AppKit 0x00007fff8d22d151 -[NSKeyBindingManager(NSKeyBindingManager_MultiClients) interpretEventAsCommand:forClient:] + 1392
26 AppKit 0x00007fff8d24c1c2 -[NSTextInputContext handleEvent:] + 845
27 AppKit 0x00007fff8d22b9dd -[NSView interpretKeyEvents:] + 180
28 AppKit 0x00007fff8d24bd6d -[NSTextView keyDown:] + 658
29 AppKit 0x00007fff8d1f856b -[NSWindow sendEvent:] + 1843
30 AppKit 0x00007fff8d199b32 -[NSApplication sendEvent:] + 3395
31 AppKit 0x00007fff8cfe99f9 -[NSApplication run] + 646
32 AppKit 0x00007fff8cfd4783 NSApplicationMain + 940
33 ResponderExperiment 0x0000000100002de2 main + 34
34 libdyld.dylib 0x00007fff8c5bf5fd start + 1
35 ??? 0x0000000000000003 0x0 + 3
)
So perhaps I could write my own NSArrayController rearrangeObjects: method, that does not loose focus?
回答1:
Here's a possible solution. The idea is to re-implement the response of the text field (or, more accurately, the field editor serving the text field) to the Tab key as two separate steps. First, end editing, which will make the array controller rearrange its content. We end editing by resetting focus, so we don't care what the array controller may happen to do to focus anyway. Then, advance the focus from the originally-focused text field ourselves.
Set a delegate on your text fields. In that delegate, implement the following method:
- (BOOL) control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)command
{
if (command == @selector(insertTab:))
{
// End editing. This will commit the edited value and cause the array controller
// to rearranged its contents.
[control.window makeFirstResponder:nil];
// This will put focus on the next control from the text field.
[control.window selectKeyViewFollowingView:control];
// Prevent the normal processing of this command
return YES;
}
if (command == @selector(insertBacktab:))
{
// End editing. This will commit the edited value and cause the array controller
// to rearranged its contents.
[control.window makeFirstResponder:nil];
// This will put focus on the previous control from the text field.
[control.window selectKeyViewPrecedingView:control];
// Prevent the normal processing of this command
return YES;
}
return NO;
}
One thing I'm not sure of: what happens if instead of Tab-ing away from the text field, you click on another text field. Does the array controller still reset focus in that case?
回答2:
In my case, I have overrided abortEditing method of NSControl, with my own logic.
- (BOOL)abortEditing {
MyObject *object = [myArrayController.arrangedObjects objectAtIndex:self.editedRow];
return ![[Manager sharedInstance] isObjectBeingEditing:object];
}
来源:https://stackoverflow.com/questions/25311016/keyboard-focus-lost-after-inserting-and-editing-new-object-when-nsarraycontroll