【问题标题】:Implement Delegate Method on NSTextField在 NSTextField 上实现委托方法
【发布时间】:2021-12-28 16:24:10
【问题描述】:

我正在尝试在 NSTextField 上实现委托方法,如 Apple 的 this article 中所述。我的目标是让NSTextField 接受回车和制表符。我在其他地方(包括链接的文章)读到NSTextView 是一个更好的选择。但是,我正在一个不支持NSTextView 的多平台框架中工作,如果我能让它接受回车,NSTextField 将完成这项工作。

根据文章,这是我的代码:

@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

此外,在文本字段的 Identity Inspector 中,我已将类名从默认的 NSTextField 更改为我的类名。但是,当我运行我的程序时,委托方法永远不会被调用。要在 Interface Builder 中进行设置,我还需要做些什么吗?

【问题讨论】:

    标签: objective-c cocoa interface-builder


    【解决方案1】:

    您链接的文档中有一些相关的部分我认为可能被忽略了。

    我复制了以下几行:

    如果您决定继续使用 NSTextField,可以通过实现以下 delegate 方法来实现允许 tab 键和/或允许 enter 和 return 键进行换行:

    • (BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector;

    注意:在你的自己的对象中实现这个delegate方法时,你应该将你的对象设置为这个NSTextField的“delegate”。

    我将一些我认为可能被遗漏的标注加粗。

    此方法在 NSControl.h 中的 NSControlTextEditingDelegate 协议中。因此,它应该由实现NSControlTextEditingDelegate(即NSTextFieldDelegate)的类实现

    这样做的一种常见方法是让 ViewController “持有” NSTextField 成为NSTextFieldDelegate

    这是一个非常简单的示例,使用您链接的 Apple 示例代码:

    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
    

    然后将您的 NSTextField 的委托设置为 ViewController

    无需添加自定义子类。

    或者,您可以将自定义文本字段作为其自己的代表的子类。大致如下:

    #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
    

    【讨论】:

    • 我最终确实使用您提供的 ViewController 模式解决了这个问题。我会将此标记为答案,谢谢。包含这样的内容可能会有所帮助,以便使 View Controller 对象可用作委托目标,您必须将视图控制器从库调色板拖到窗口的左侧列,然后在检查器中更改其类。 (也就是说,假设我对 IB 一无所知,它进入了我接近的这个项目。)另外,我没有在我的实现中包含 viewDidLoad 或 setRepresentedObject,但它似乎工作。有问题吗?
    • @rpatters1 很高兴答案有所帮助。在 Xcode 中创建新的 macOS 应用程序时,它会创建一个默认的 ViewController 类,这是我在此处的示例中使用的。 viewDidLoad 和 setRepresentedObject 会自动添加到这个默认类中(这就是我将它们留在这里的原因),尽管它不应该像你所说的那样改变功能。
    • 我正在为主机应用程序开发一个插件,该插件为对话框提供跨平台支持。因此,在我添加之前,我的项目根本没有 View Controller。
    猜你喜欢
    • 1970-01-01
    • 2012-04-02
    • 2019-08-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多