Implement Delegate Method on NSTextField

Viewed 105

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?

1 Answers

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

Set_Text_Field_Delegate

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
Related