NSTableView缺少allowsSelectionDuringEditing属性的技术咨询
Great question—this is such a common gotcha when moving between iOS and macOS table views, since their interaction models are way different under the hood. Let’s break this down step by step:
1. Why doesn’t NSTableView have allowsSelectionDuringEditing?
The short answer comes down to platform-specific design philosophies:
- On iOS, table view editing is row-focused (think swipe-to-delete or edit-mode reorder controls). By default, selection is disabled during editing to avoid conflicting with these row-level actions, so
allowsSelectionDuringEditingwas added as an opt-in to re-enable it. - On macOS, table views are cell-centric rather than row-centric. The default behavior assumes users might want to switch between rows even while editing a cell (e.g., copying data from another row mid-edit). Apple didn’t add a dedicated
allowsSelectionDuringEditingproperty because this "allow selection during editing" behavior is the out-of-the-box state. Your frustration likely stems from needing the opposite: disabling selection while editing, which isn’t a built-in toggle.
2. Implementing allowsSelectionDuringEditing in a custom NSTableView
You can build this property yourself by subclassing NSTableView and overriding the core selection control method. Here’s how:
First, add the custom property to your subclass:
// CustomNSTableView.h @property (nonatomic, assign) BOOL allowsSelectionDuringEditing;
// CustomNSTableView.swift var allowsSelectionDuringEditing: Bool = true
Then override selectionCanChange—this is the method that controls whether the table allows selection changes to happen:
// CustomNSTableView.m - (BOOL)selectionCanChange { // Check if we're actively editing a cell BOOL isEditing = (self.editingRow != -1 && self.editingColumn != -1); // If editing, respect our custom property; otherwise use default behavior if (isEditing) { return self.allowsSelectionDuringEditing; } return [super selectionCanChange]; }
// CustomNSTableView.swift override var selectionCanChange: Bool { let isEditing = editingRow != -1 && editingColumn != -1 return isEditing ? allowsSelectionDuringEditing : super.selectionCanChange }
This mimics UITableView’s behavior exactly: when editing, selection is allowed only if allowsSelectionDuringEditing is true. You can set this property in Interface Builder or code just like you would on iOS.
3. Handling NSSegmentedControl with allowSelectionWhenFocused
For your segmented control column, you need to block row selection changes only when that control is focused, but still let it handle its own keyboard input (←, →, space). Here’s a clean approach:
Step 1: Add a custom property to your NSSegmentedControl subclass
First, subclass NSSegmentedControl to add a toggle for this behavior. Let’s name it clearly to avoid confusion:
// CustomSegmentedControl.h // Set this to YES to block row selection when the control is focused @property (nonatomic, assign) BOOL preventsRowSelectionWhenFocused;
// CustomSegmentedControl.swift // Set this to true to block row selection when the control is focused var preventsRowSelectionWhenFocused: Bool = true
Step 2: Update your CustomNSTableView’s selection logic
Modify the selectionCanChange method to check if the current first responder is your custom segmented control, and if its toggle is enabled:
- (BOOL)selectionCanChange { BOOL isEditing = (self.editingRow != -1 && self.editingColumn != -1); // Check if first responder is our segmented control that blocks selection NSResponder *firstResponder = self.window.firstResponder; if ([firstResponder isKindOfClass:[CustomSegmentedControl class]]) { CustomSegmentedControl *segmentedControl = (CustomSegmentedControl *)firstResponder; if (segmentedControl.preventsRowSelectionWhenFocused) { return NO; // Block selection changes entirely } } if (isEditing) { return self.allowsSelectionDuringEditing; } return [super selectionCanChange]; }
override var selectionCanChange: Bool { let isEditing = editingRow != -1 && editingColumn != -1 // Check for our segmented control if let segmentedControl = window?.firstResponder as? CustomSegmentedControl, segmentedControl.preventsRowSelectionWhenFocused { return false } return isEditing ? allowsSelectionDuringEditing : super.selectionCanChange }
Step 3: Ensure the segmented control handles keyboard events correctly
Don’t use refuseFirstResponder—that would prevent the control from receiving keyboard input entirely, which breaks its interactivity. Instead, let the control become first responder (it does this by default) and make sure it processes its own key events. If you notice keyboard events leaking to the table, override keyDown in your segmented control subclass to prioritize its own actions:
- (void)keyDown:(NSEvent *)event { NSString *characters = event.charactersIgnoringModifiers; // Handle space, return, left/right arrows if ([characters isEqualToString:@"\r"] || [characters isEqualToString:@" "] || event.keyCode == 123 || event.keyCode == 124) { [super keyDown:event]; // Let the control handle these actions } else { [super keyDown:event]; // Pass other keys to the responder chain if needed } }
override func keyDown(with event: NSEvent) { let keyCode = event.keyCode let characters = event.charactersIgnoringModifiers ?? "" if characters == "\r" || characters == " " || keyCode == 123 || keyCode == 124 { super.keyDown(with: event) } else { super.keyDown(with: event) } }
(Note: Key codes 123/124 map to left/right arrows on most Mac keyboards—you can use NSEvent constants for safer checking if needed.)
When to subclass which component?
A quick rule of thumb to avoid trial and error:
- NSTableView: Use for global behavior like selection rules, editing state management, or overriding table-wide responder chain logic. This is where you’d add
allowsSelectionDuringEditing. - NSTableRowView: Use for row-specific visuals (e.g., custom selection highlights) or row-level event handling (e.g., ignoring clicks on certain parts of the row). Not ideal for your selection control needs.
- NSTableCellView: Use for arranging cell content, connecting IBOutlets, or handling cell-level layout. You’d assign your custom segmented control here in Interface Builder.
- NSSegmentedControl: Use when you need to add custom properties (like
preventsRowSelectionWhenFocused) or override control-specific behavior (like handling keyboard input).
About refuseFirstResponder
You were right to be confused—this method tells the system "don’t make me the first responder," which is the opposite of what you want here. If you return YES from refuseFirstResponder, your segmented control can’t receive keyboard focus at all, breaking its interactivity. Only use this if you want a control to never be focusable.
Beyond Apple’s docs: Resources to check out
- WWDC Videos: WWDC 2015’s Table View Best Practices and WWDC 2019’s Modernizing Table Views dive deep into macOS table view behavior and customization, with real-world examples.
- Apple Developer Forums: Search for threads like "NSTableView disable selection during editing" or "NSSegmentedControl prevent row selection"—many developers have solved the exact same problems, and you’ll find actionable code snippets and workarounds.
- Open Source Examples: Check out macOS apps on GitHub that use editable table views with custom selection logic. For example, MASPreferences has a robust table view implementation, and CocoaPods uses custom tables for its interface.
内容的提问来源于stack exchange,提问作者griftopia

