我的应用中有一个相当普通的源列表(从对象库中拖出),其中NSTreeController
作为其数据源。我将 DataCell 中的NSTextField
设置为可编辑,但我希望能够为某些单元格关闭它。我认为你这样做的方式是使用NSTextField
的委托,但是没有一个我试过的委托方法被调用。有什么我想念的吗?我的代理人在我的XIB中设置了一个插座,它恰好是所有者NSOutlineView
的委托,同时实施了NSOutlineViewDelegate
和NSTextFieldDelegate
协议。
另外,我也不能使用旧的–outlineView:shouldEditTableColumn:item:
NSOutlineViewDelegate
方法,因为这只适用于基于单元格的大纲视图(我假设是这种情况 - 大纲虽然类似的NSTableView
文档有,但看起来文档似乎没有为Lion更新过,而且这些方法也没有被调用。
我在一个全新的测试项目中重现了这一点,所以它绝对与我的任何自定义类无关。按照以下步骤创建我的示例项目,并重现此问题。
Source List
拖到窗口Object
拖到停靠栏(窗口左侧)上,为其中一个指定SourceListDataSource
类,为另一个指定SourceListDelegate
dataSource
和delegate
出口连接到这两个对象NSTextField
Value
绑定,保持默认设置delegate
出口连接到“源列表委派”对象Behavior
属性设置为可编辑预期:该字段不可编辑,并且有一个“好吧,我应该吗?”日志中的消息
实际:该字段可编辑,不会记录任何消息
这是框架中的错误,还是我应该以不同的方式实现这一目标?
#import <Cocoa/Cocoa.h>
@interface SourceListDataSource : NSObject <NSOutlineViewDataSource>
@property (retain) NSArray *items;
@end
@implementation SourceListDataSource
@synthesize items;
- (id)init
{
self = [super init];
if (self) {
items = [[NSArray arrayWithObjects:@"Alo", @"Homora", nil] retain];
}
return self;
}
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
if (!item) {
return [self.items objectAtIndex:index];
}
return nil;
}
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
return !item ? self.items.count : 0;
}
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
return NO;
}
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
return item;
}
@end
#import <Foundation/Foundation.h>
@interface SourceListDelegate : NSObject <NSOutlineViewDelegate, NSTextFieldDelegate> @end
@implementation SourceListDelegate
- (NSTableRowView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item {
return [outlineView makeViewWithIdentifier:@"DataCell" owner:self];
}
- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor {
NSLog(@"well, should I?");
return NO;
}
@end
答案 0 :(得分:3)
子类NSTableCellView
,带有文本字段的出口,并在awakeFromNib
中设置文本字段委托。完成后,control:textShouldBeginEditing:
被调用。我不知道为什么,但是(编辑:) 如果你在xib中设置委托,委托方法不会被调用 - 我有和你一样的经历。
或者,您可以放弃委托并使用绑定有条件地将Editable设置为模型的布尔属性,或者使用作用于模型实例并返回布尔值的值转换器。使用文本字段的可编辑绑定。
答案 1 :(得分:0)
我也遇到过这个问题。因为我不想丢失绑定,所以我做了以下事情:
将TextField的editable
绑定到objectValue
并设置自定义NSValueTransformer
子类。
答案 2 :(得分:0)
上述其他提议的解决方案性能不佳,无法在现代版本的 macOS 上运行。 NSTableView
在整个表格中的每个 textField 即将被编辑时调用 acceptsFirstResponder
。当您在表格中滚动时,第一响应者方法会被调用。如果您在这些调用中进行一些日志记录,您就会看到它们正在运行。
此外,不需要将 textField 的委托分配给 IB 以外的任何地方,并且实际上不会起作用,因为 NSTableView
(因此 NSOutlineView
)基本上“接管”了它们包含的视图。>
子类 NSTableView
(或 NSOutlineView
)并执行以下操作:
final class MyTableView: NSTableView
{
override func validateProposedFirstResponder(_ responder: NSResponder, for event: NSEvent?) -> Bool
{
// NSTableView calls -validateProposedResponder on cellViews' textFields A METRIC TON, even while just scrolling around, therefore
// do not interfere unless we're evaluating a CLICK on a textField.
if let textField: NSTextField = responder as? NSTextField,
(event?.type == .leftMouseDown || event?.type == .rightMouseDown)
{
// Don't just automatically clobber what the TableView returns; it'll return false here when delays are needed for double-actions, etc.
let result: Bool = super.validateProposedFirstResponder(responder, for: event)
// IF the tableView thinks this textField should edit, now we can ask the textField's delegate to confirm that.
if result == true
{
print("Validate first responder called: \(responder).")
return textField.delegate?.control?(textField, textShouldBeginEditing: textField.window?.fieldEditor(true, for: nil) ?? NSText()) ?? result
}
return result
}
else
{
return super.validateProposedFirstResponder(responder, for: event)
}
}
}
这是针对 macOS 11.3.1 和 Xcode 12.5 为面向 macOS 11 的应用程序编写的。
NSTableCellViews 中 NSTextFields 的 isEditable
属性必须设置为 true
。 NSTableView 的 -validateFirstResponder 实现将首先检查该属性,因此您无需在委托方法中这样做。