I am attempting to implement a delegate method on NSTextField
as described in this article from Apple. My goal is for the NSTextField
to accept carriage returns and tabs. I have read elsewhere (including the linked article) that NSTextView
is a better choice. However, I am working within a multiplatform framework that lacks support for NSTextView
, and NSTextField
will do the job if I can get it to accept carriage returns.
Based on the article, here is my code:
@interface MyTextFieldSubclass : NSTextField
{}
- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector;
@end
@implementation MyTextFieldSubclass
- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector
{
BOOL result = NO;
if (commandSelector == @selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
else if (commandSelector == @selector(insertTab:))
{
// tab action:
// always insert a tab character and don’t cause the receiver to end editing
[textView insertTabIgnoringFieldEditor:self];
result = YES;
}
return result;
}
@end
Additionally, in the Identity Inspector of the text field, I have changed the class name from the default NSTextField
to my class name. However, when I run my program, the delegate method never gets called. Is there something else I have to do to set this up in Interface Builder?
There are a few parts of the documentation you linked which is pertinent that I think may have been neglected.
I've copied a few of the lines below:
Should you decide to keep using NSTextField, allowing the tab key and/or allowing enter and return keys for line-breaks can be achieved by implementing the following delegate method:
- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector;
Note: When implementing this delegate method in your own object you should set your object up as the "delegate" for this NSTextField.
I've bolded a few of the callouts which I think might have been missed.
This method is within the NSControlTextEditingDelegate
protocol within NSControl.h. As such it should be implemented by a class which implements the NSControlTextEditingDelegate
(i.e. NSTextFieldDelegate
)
One common way of doing this is to have the ViewController "holding" the NSTextField be the NSTextFieldDelegate
.
Here's a very simple example using the sample code from Apple you linked:
ViewController.h
#import <Cocoa/Cocoa.h>
@interface ViewController : NSViewController <NSTextFieldDelegate>
@end
ViewController.m
#import "ViewController.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
// Update the view, if already loaded.
}
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
BOOL result = NO;
if (commandSelector == @selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
else if (commandSelector == @selector(insertTab:))
{
// tab action:
// always insert a tab character and don’t cause the receiver to end editing
[textView insertTabIgnoringFieldEditor:self];
result = YES;
}
return result;
}
@end
Then set your NSTextField's delegate to the ViewController
No need to add a custom subclass.
Alternatively you could probably make the custom text field subclass its own delegate. Something along these lines:
#import "MyTextFieldSubclass.h"
@interface MyTextFieldSubclass() <NSTextFieldDelegate>
@end
@implementation MyTextFieldSubclass
- (instancetype)init {
self = [super init];
if (self) {
self.delegate = self;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
self.delegate = self;
}
return self;
}
- (instancetype)initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
if (self) {
self.delegate = self;
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
}
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
BOOL result = NO;
if (commandSelector == @selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
else if (commandSelector == @selector(insertTab:))
{
// tab action:
// always insert a tab character and don’t cause the receiver to end editing
[textView insertTabIgnoringFieldEditor:self];
result = YES;
}
return result;
}
@end