2

我已经尝试了一个简单的第一个 NSTokenField 示例,它使用基于文档的 ARC 使用应用程序,使我的 Document 类成为 NSTokenFieldDelegate。它可以工作,但有一个原因:委托方法 tokenField:completionsForSubstring:indexOfToken:indexOfSelectedItem: 只看到 indexOfToken 的值为 0,即使我成功编辑了一个不是标记字符串中第一个标记的标记。我在 OS X 10.8.2 上使用 XCode 4.5 和 10.8 框架。

问题:为什么总是0?我希望它是用户正在编辑的字段中间接看到的标记数组 0 .. n - 1 中标记的索引。

要重现,如上所述启动项目并添加以下文本,然后使用 XIB 编辑器并将 NSTokenField 拖到文档窗口上,将令牌字段设置为文档的令牌字段,并使文档实例成为令牌字段的代表。

文档.h

#import <Cocoa/Cocoa.h>

@interface Document : NSDocument <NSTokenFieldDelegate>
{
    IBOutlet NSTokenField *tokenField;  // of (Token *).
    NSMutableDictionary *tokens;        // of (Token *).
}
@end

令牌.h

#import <Foundation/Foundation.h>

@interface Token : NSObject
@property (strong, nonatomic) NSString *spelling;
- (id)initWithSpelling:(NSString *)s;
@end

令牌.m

#import "Token.h"

@implementation Token
@synthesize spelling;

- (id)initWithSpelling:(NSString *)s
{
    self = [super init];
    if (self)
        spelling = s;
    return self;
}

@end

文档.m

#import "Document.h"
#import "Token.h"

@implementation Document

- (id)init
{
    self = [super init];
    if (self) {
        tokens = [NSMutableDictionary dictionary];
    }
    return self;
}

...

#pragma mark NSTokenFieldDelegate methods

- (NSArray *)tokenField:(NSTokenField *)tokenField
completionsForSubstring:(NSString *)substring
           indexOfToken:(NSInteger)tokenIndex
    indexOfSelectedItem:(NSInteger *)selectedIndex
{
    NSLog(@"tokenField:completionsForSubstring:\"%@\" indexOfToken:%ld indexOfSelectedItem:",
          substring, tokenIndex);
    NSMutableArray *result = [NSMutableArray array];
    for (NSString *key in tokens) {
        //NSLog(@"match? \"%@\"", key);
        if ([key hasPrefix:substring])
            [result addObject:key];
    }
    return result;
}

- (id)tokenField:(NSTokenField *)tokenField representedObjectForEditingString:(NSString *)editingString
{
    NSLog(@"tokenField:representedObjectForEditingString:\"%@\"", editingString);
    Token *token;
    if ((token = [tokens objectForKey:editingString]) == nil) {
        token = [[Token alloc] initWithSpelling:editingString];
        [tokens setObject:token forKey:editingString];
        //NSLog(@"token %@", [token description]);
        NSLog(@"tokens %@", [tokens description]);
    }
    return token;
}

- (NSString *)tokenField:(NSTokenField *)tokenField displayStringForRepresentedObject:(id)representedObject
{
    NSString *spelling = [representedObject spelling];
    NSLog(@"tokenField:displayStringForRepresentedObject: = \"%@\"", spelling);
    return spelling;
}

@end

令牌的输入以换行符或逗号字符终止。

4

2 回答 2

1

这绝对是您应该向 Apple 报告的事情。

为了确定 中已编辑标记的索引NSTokenField,我首先创建了 的子类,NSTextView这样我就有了一个自定义字段编辑器。(如果你走这条路,不要忘记将你的NSTextView实例设置为字段编辑器-[NSTextView setFieldEditor:]。)然后,我将 子类化NSTokenFieldCell,覆盖-[NSCell fieldEditorForView:]

  1. 创建我的NSTextView子类的实例,
  2. 将此NSTextView实例设置为self的委托,并且
  3. 返回此NSTextView实例。

tokenFieldCell:completionsForSubstring:indexOfToken:indexOfSelectedItem:在您的子类中实现NSTextView

- (NSArray *)tokenFieldCell:(NSTokenFieldCell *)tokenFieldCell completionsForSubstring:(NSString *)substring indexOfToken:(NSInteger)tokenIndex indexOfSelectedItem:(NSInteger *)selectedIndex
{
    // The tokenIndex passed to this function seems to be 0 in all cases, so we
    // need to determine the tokenIndex ourselves. The range returned by
    // NSText's selectedRange method treats non-plain-text tokens as if they
    // have unit length. So, for each token, subtract from the range location
    // either the token's length if it's a plain text token, or 1 if it's any
    // other style of token. Each time we subtract from the range location,
    // increment tokenIndex. When the range location becomes less than or equal
    // to 0, tokenIndex will be the index of the edited token.
    tokenIndex = 0;
    NSInteger rangeLocation = self.selectedRange.location;
    for (id token in tokenFieldCell.objectValue) {
        if ([self tokenFieldCell:tokenFieldCell styleForRepresentedObject:token] == NSPlainTextTokenStyle) {
            rangeLocation -= [self tokenFieldCell:tokenFieldCell displayStringForRepresentedObject:token].length;
        } else {
            rangeLocation--;
        }
        if (rangeLocation > 0) {
            tokenIndex++;
        } else {
            break;
        }
    }
}

selectedRange这里的想法是利用计算an的事实,NSTextView假设非纯文本标记的长度为 1。通过从selectedRange位置减去标记的长度直到位置为负,我们可以确定标记索引。

请注意,您的NSTextView子类还必须实现tokenFieldCell:displayStringForRepresentedObject:tokenFieldCell:styleForRepresentedObject:使其正常工作。

于 2012-10-26T22:33:01.907 回答
1

这是我的快速解决方案,对我来说效果很好。只需将此添加到您的viewController.

     func tokenIndex() -> Int {
        var index = 0
        let range = self.tokenField?.currentEditor()?.selectedRange
        let rangeLocation = range?.location ?? 0

        let string = self.tokenField?.currentEditor()?.string as NSString?
        if let subString = string?.substring(to: rangeLocation) as NSString? {
            let maxLimit = subString.length

            for i in 0..<maxLimit {
                //each token is represented as NSAttachmentCharacter,
                //so count it till current selected range
                if subString.character(at: i) == unichar(NSAttachmentCharacter) {
                    index += 1
                }
            }
        }

        return index
    }
于 2017-07-25T19:02:27.397 回答