3

我有一个NSTokenField向对象(文档)添加标签。我想在将令牌添加到令牌字段时(输入令牌化字符时)用新标签更新对象。不幸的是,这似乎不起作用。NSTokenField连接到我的控制器中的一个动作,但这个动作方法永远不会被调用。

我也有一个NSTextField以相同方式连接到控制器的控制器,它在控制器中的操作方法被调用。

我也用键值观察试过这个:

- (void) awakeFromNib {
    [tokenField addObserver:self forKeyPath:@"objectValue" options:NSKeyValueObservingOptionNew context:NULL];
}


- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if([object isEqual:tokenField]){
        NSLog(@"Tokens changed");
    }
}

但是仅当我以编程方式更改令牌时才会调用此操作。

更改令牌时如何通知我tokenField

4

2 回答 2

8

NSTokenField创建新标签时不会调用操作选择器。根据您在Interface Builder中使用的设置,当您按 Enter 键结束编辑(Send On Enter Only)或以其他方式结束编辑(Send On End Editing)时,它会被调用。为了获得良好的控制,您需要另一种方法。


将标记字符添加到标记字段时出现的蓝色标记NSTextAttachment称为文本附件(的实例)。确定何时从令牌字段添加/删除标签的一种方法是跟踪令牌字段的基础属性字符串中包含的这些对象的数量的变化。

要访问相关的属性字符串,您需要获取fieldEditor layoutManager - 最终提供出现在文本视图中的字符串的对象。一旦你得到它,每次你收到一条消息,计算它的表示中controlTextDidChange:的文本附件的数量。如果这一次的数字大于上次计数中记录的数字,则刚刚添加了一个标签。stringattributedString

#import "AppDelegate.h"

@interface AppDelegate ()

@property (weak) IBOutlet NSWindow *window;
@property (weak) NSLayoutManager *lm;
@property (nonatomic) NSUInteger tokenCount;

@end

@implementation AppDelegate

// The text in the fieldEditor has changed. If the number of attachments in the
// layoutManager's attributedString has changed, either a new tag has been added,
// or an existing tag has been deleted.
-(void)controlTextDidChange:(NSNotification *)obj {
    NSUInteger updatedCount = [self countAttachmentsInAttributedString:self.lm.attributedString];
    if (updatedCount > self.tokenCount) {
        NSLog(@"ADDED");
        self.tokenCount = updatedCount;
    } else if (updatedCount < self.tokenCount) {
        NSLog(@"REMOVED");
        self.tokenCount = updatedCount;
    }
}

// About to start editing - get access to the fieldEditor's layoutManager
-(BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor {
    self.lm = [(NSTextView *)fieldEditor layoutManager];
    return YES;
}

// Iterate through the characters in an attributed string looking for occurrences of
// the NSAttachmentCharacter.
- (NSInteger)countAttachmentsInAttributedString:(NSAttributedString *)attributedString {
    NSString *string = [attributedString string];
    NSUInteger maxIndex = string.length - 1;
    NSUInteger counter = 0;

    for (int i = 0; i < maxIndex + 1; i++) {
        if ([string characterAtIndex:i] == NSAttachmentCharacter) {
            counter++;
        }
    }
    return counter;
}

@end
于 2015-02-11T00:45:38.033 回答
1

将@paul-patterson 的代码移植到 Swift 3:

override func controlTextDidChange(_ obj: Notification) {
    guard let fieldEditor = self.tokenField.currentEditor() as? NSTextView,
        let layoutManager = fieldEditor.layoutManager
        else { return }

    func countAttachments(attributedString: NSAttributedString) -> Int {

        let string = attributedString.string as NSString
        let maxIndex = string.length - 1
        var counter = 0

        for i in 0..<maxIndex {
            if string.character(at: i) == unichar(NSAttachmentCharacter) {
                counter += 1
            }
        }

        return counter
    }

    let currentCount = countAttachments(attributedString: layoutManager.attributedString())
    // cache count or act on it directly
}

奇怪的是,以下在 Swift 中不会产生预期的结果:

layoutManager.attributedString().string
    .split(by: Character(UnicodeScalar(NSAttachmentCharacter)!)).count

相反,当用户不键入时它返回 0,而当正在编辑标记时它返回 1。

let isEditing = layoutManager.attributedString().string
    .split(by: Character(UnicodeScalar(NSAttachmentCharacter)!)).count == 1

结合这两种方法,您可以使用状态机编写自定义的“添加/删除令牌”回调。(不过,我认为这不是一种非常安全的实现方式。)

  • 使用 跟踪令牌计数countAttachments(attributedString:)
  • 用于isEditing检查...
    1. 如果用户开始添加新注释(新计数 > 旧计数 && isEditing == true)
    2. 如果用户开始编辑现有笔记(新计数 == 旧计数 && isEditing == true)
    3. 如果用户完成了一个令牌(oldIsEditing == true && newIsEditing == false)
于 2017-05-29T06:50:05.680 回答