I have a fairly vanilla Source List (dragged out from the Object Library) in my app, with an NSTreeController
as its data source. I set the NSTextField
inside the DataCell to be editable, but I want to be able to turn that off for some cells. The way I figured you would do this, is with a delegate for the NSTextField
, but none of the delegate methods I've tried get called. Is there something I'm missing? I have the delegate set with an outlet in my XIB, and it happens to be the delegate to the owner NSOutlineView
, as well, implementing both the NSOutlineViewDelegate
and NSTextFieldDelegate
protocols.
Also, I can't use the old –outlineView:shouldEditTableColumn:item:
NSOutlineViewDelegate
method either, since that only works with cell-based Outline Views (I'm assuming this is the case - the Outline View documentation doesn't appear to have been updated for Lion, though the analogous NSTableView
documentation has, and those methods don't get called either).
I reproduced this in a brand new test project, so it's definitely not related to any of my custom classes. Follow the steps below to create my sample project, and reproduce this problem.
Source List
onto the WindowObject
s onto the dock (left side of the window), specifying the SourceListDataSource
class for one, and the SourceListDelegate
for the otherdataSource
and delegate
outlets to those two objectsNSTextField
for the DataCell view inside the outline view's columnValue
binding, keeping the default settingsdelegate
outlet to the Source List Delegate objectBehavior
property to EditableExpected: The field is not editable, and there is a "well, should I?" message in the log
Actual: The field is editable, and no messages are logged
Is this a bug in the framework, or am I supposed to achieve this a different way?
#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
Subclass NSTableCellView
, with an outlet for the text field, and set the text field delegate in awakeFromNib
. After doing that, control:textShouldBeginEditing:
gets called. I'm not sure why, but (edit:) if you set the delegate in the xib, the delegate methods aren't called – I had the same experience as you.
Alternatively, you can forego the delegate and conditionally set Editable using a binding, either to a boolean property of the model, or using a value transformer which acts on a model instance and returns a boolean. Use the Editable binding of the text field.