2012-10-03 205 views
2

我已經嘗試了一個基於文檔的使用ARC的應用程序,使我的Document類成爲NSTokenFieldDelegate的NSTokenField的簡單第一個示例。它的工作原理是:委託方法tokenField:completionsForSubstring:indexOfToken:indexOfSelectedItem:即使我成功編輯了不是標記字符串中第一個標記的標記,它也從不會看到indexOfToken爲0的任何內容。我在OS X 10.8.2上使用10.8框架的XCode 4.5。NSTokenField的tokenField:completionsForSubstring:indexOfToken:indexOfSelectedItem:indexOfToken始終爲零

問題:爲什麼總是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 

條目被終止以換行或逗號字符。

+0

這似乎仍然是一個問題。我想知道爲什麼它尚未修復。 – adev

+0

我解決了這個問題,稍後會發布答案。 – adev

+0

發表我的工作答案在這裏迅速https://stackoverflow.com/a/45311580/8234523 – adev

回答

1

這絕對看起來像你應該向蘋果報告。

要確定NSTokenField中已編輯的令牌的索引,我首先創建了一個NSTextView的子類,以便我有一個自定義字段編輯器。 (如果你走這條路線,不要忘記設置您的NSTextView實例是一個字段編輯器-[NSTextView setFieldEditor:])。然後,我子類NSTokenFieldCell,覆蓋-[NSCell fieldEditorForView:]

  1. 創建我NSTextView子類的實例,
  2. 將此NSTextView設置爲self的代表,並且
  3. 返回此NSTextView實例。

NSTextView子類中實現tokenFieldCell:completionsForSubstring:indexOfToken:indexOfSelectedItem:

- (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; 
     } 
    } 
} 

這裏的想法是使用一個NSTextViewselectedRange計算假設非純文本標記爲1的長度的事實。通過從selectedRange位置減去令牌長度,直到位置爲負數,我們可以確定令牌索引。

請注意,您的NSTextView子類也必須實現tokenFieldCell:displayStringForRepresentedObject:tokenFieldCell:styleForRepresentedObject:才能正常工作。

+0

這不適合我自從styleForRepresentedObject:總是返回我們在委託中設置的樣式。如果我們正在編輯一個標記,selectedRange將會改變,但是style始終是相同的,因此它會一直到其他部分。 – adev

+0

@adev如果你的NSTextView子類'tokenFieldCell:styleForRepresentedObject:'的實現總是返回'NSDefaultTokenStyle',那麼是的,只有'rangeLocation - '會運行。不過,我認爲這仍然會返回正確的標記索引,因爲具有'NSDefaultTokenStyle'風格的標記的長度始終爲1. – Nate

+0

不,在輸入文本時,其長度不是1,而是長度爲鍵入的字符長度。所以我採取了不同的方法。請檢查我的答案。 – adev

0

這是我在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 
    }