如何制作包含属性文本但仅复制纯文本的 NSTextField

How can I make an NSTextField containing attributed text but only copy plain text

我有一个 NSTextField 子类,它使用一个包含属性字符串的 NSTextFieldCell 子类。我希望当用户复制文本时它被复制到没有属性的粘贴板。

我已经尝试子类化 NSTextView、覆盖 copy: 并将 isFieldEditor 设置为 YES 并从单元格 fieldEditorForView 方法返回这个新编辑器。虽然这只会复制纯文本,但无论何时使用,我都会在文本上绘制文本(等等......),如果底层属性字符串被另一个控件更改,则字段编辑器保持不变。当我不使用 MyFieldEditor 并让 NSTextFieldCell 子类使用默认实现时,不会发生此问题。

这个问题有更简单的解决方案吗?

我是否需要覆盖或接收委托消息的其他内容?

MyFieldCell.m

- (NSTextView *)fieldEditorForView:(NSView *)controlView
{
    MyFieldEditor *editor = [[MyFieldEditor alloc] init];
    [super setUpFieldEditorAttributes:editor];

    return editor;
}

MyFieldEditor.m

@implementation MyFieldEditor

- (instancetype)init
{
    if ( (self = [super init]) )
    {
        [self setFieldEditor:YES];
    }

    return self;
}

- (NSString *)selectedString
{
    return [[self string] substringWithRange:[self selectedRange]];
}

- (void)copy:(id)sender
{
    [[NSPasteboard generalPasteboard] setString:[self selectedString] forType:NSPasteboardTypeString];
}

@end

注意:我正在使用 ARC。

显示问题图像

请在下面找到应该更改的内容。 测试并与 Xcode 11.2.1 / macOS 10.15.2 一起使用,没有显示问题。

a) 不需要自定义 NSTextFieldCellNSTextField,所以只使用默认

b) 在 MyFieldEditor.m

中更改为以下内容
- (void)copy:(id)sender
{
    [NSPasteboard.generalPasteboard declareTypes:@[NSPasteboardTypeString] owner:self];
    [NSPasteboard.generalPasteboard setString:[self selectedString] forType:NSPasteboardTypeString];
}

c) 添加 window 委托方法,用字段编辑器替换目标文本字段(这是提供自定义字段编辑器的有效记录方式)

- (nullable id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(nullable id)client {
    if (client == self.textField) { // << in this case it is outlet
        return MyFieldEditor.new;
    }
    return nil;
}

更新:

如下覆盖 NSTextFieldCell 并在 XIB 中为目标 NSTextField 分配它而不是上面的 NSWindow 委托方法给出相同的有效行为。

@interface MyTextCell: NSTextFieldCell
@end

@implementation MyTextCell
- (nullable NSTextView *)fieldEditorForView:(NSView *)controlView {
    id editor = MyFieldEditor.new;
    [super setUpFieldEditorAttributes:editor];
    return editor;
}
@end

问题是 fieldEditorForView: 被重复调用。每次调用它都会创建一个新的 MyFieldEditor 并且不会删除旧的字段编辑器。

一个NSWindow只有一个重复使用的字段编辑器。我想假设是给定类型的任何字段编辑器都应该只创建一次并且应该重复使用。因此,引用编辑器的静态变量和 nil 创建检查提供了一个惰性可重用编辑器。

@interface MyFieldCell: NSTextFieldCell
@end

@implementation MyFieldCell
- (nullable NSTextView *)fieldEditorForView:(NSView *)controlView
{
    static MyFieldEditor *kEditor;

    if ( kEditor ) { return kEditor; }
    kEditor = [[MyFieldEditor alloc] init];

    return kEditor;
}
@end

如果编辑器能弱化就好了。不幸的是 NSTextView 及其子类与弱引用不兼容。如果有人对此有切肉刀的想法,我会洗耳恭听。