NSTokenField tokenField: завершений ForSubstring:indexOfToken:indexOfSelectedItem: indexOfToken всегда ноль
Я попробовал простой первый пример NSTokenField с приложением, использующим ARC на основе документов, в результате чего мой класс Document стал NSTokenFieldDelegate. Это работает, но с одной стороны: метод делегата tokenField: завершений ForSubstring:indexOfToken:indexOfSelectedItem: никогда не видит ничего, кроме 0 для indexOfToken, даже когда я успешно редактирую токен, который не является первым в строке токенов. Я использую XCode 4.5 на OS X 10.8.2 с платформой 10.8.
Вопрос: почему всегда 0? Я ожидаю, что это будет индекс токена в косвенно видимом массиве токенов 0 .. n - 1 в редактируемом пользователем поле.
Для воспроизведения запустите проект, как указано выше, и добавьте текст ниже, затем используйте редактор XIB и перетащите NSTokenField в окно документа, установите поле токена в качестве tokenField документа и сделайте экземпляр документа делегатом поля токена.
Document.h:
#import <Cocoa/Cocoa.h>
@interface Document : NSDocument <NSTokenFieldDelegate>
{
IBOutlet NSTokenField *tokenField; // of (Token *).
NSMutableDictionary *tokens; // of (Token *).
}
@end
Token.h:
#import <Foundation/Foundation.h>
@interface Token : NSObject
@property (strong, nonatomic) NSString *spelling;
- (id)initWithSpelling:(NSString *)s;
@end
Token.m:
#import "Token.h"
@implementation Token
@synthesize spelling;
- (id)initWithSpelling:(NSString *)s
{
self = [super init];
if (self)
spelling = s;
return self;
}
@end
Document.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
Ввод токенов завершается символом новой строки или запятой.
2 ответа
Это определенно похоже на то, что вы должны сообщить в Apple.
Чтобы определить индекс отредактированного токена в NSTokenField
Я впервые сделал подкласс NSTextView
так что у меня есть редактор пользовательских полей. (Если вы идете по этому маршруту, не забудьте настроить свой NSTextView
экземпляр, чтобы быть полевым редактором с -[NSTextView setFieldEditor:]
.) Затем я подкласс NSTokenFieldCell
переопределяя -[NSCell fieldEditorForView:]
в
- создать экземпляр моего
NSTextView
подкласс, - установить это
NSTextView
экземпляр будетself
делегат, и - вернуть это
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
из NSTextView
рассчитывается исходя из предположения, что токены непростого текста имеют длину 1. Вычитая длины токенов из selectedRange
местоположение, пока местоположение не является отрицательным, мы можем определить индекс токена.
Обратите внимание, что ваш NSTextView
Подкласс должен также реализовать tokenFieldCell:displayStringForRepresentedObject:
а также tokenFieldCell:styleForRepresentedObject:
чтобы это работало.
Вот мое решение в Swift, которое работает хорошо для меня. Просто добавьте это к вашему 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
}