I want to have an NSTokenField
that contains both plain text and tokens. That's the same problem as in this question, but the answers there haven't solved it for me. Maybe I'm missing something, or maybe Apple changed something in the 5 years since those answers were posted.
Specifically, let's say I want to type "hello%tok%" and have it turn into this:
In order to try to remove chances for confusion, I always use a custom represented object, of one of the following classes, rather than a plain string...
@interface Token : NSObject
@end
@implementation Token
@end
@interface WrappedString : NSObject
@property (retain) NSString* text;
@end
@implementation WrappedString
@end
Here are my delegate methods:
- (NSString *)tokenField:(NSTokenField *)tokenField
displayStringForRepresentedObject:(id)representedObject
{
NSString * displayString = nil;
if ([representedObject isKindOfClass: [WrappedString class]])
{
displayString = ((WrappedString*)representedObject).text;
}
else
{
displayString = @"TOKEN";
}
return displayString;
}
- (NSTokenStyle)tokenField:(NSTokenField *)tokenField
styleForRepresentedObject:(id)representedObject
{
NSTokenStyle theStyle = NSPlainTextTokenStyle;
if ([representedObject isKindOfClass: [Token class]])
{
theStyle = NSRoundedTokenStyle;
}
return theStyle;
}
- (NSString *)tokenField:(NSTokenField *)tokenField
editingStringForRepresentedObject:(id)representedObject
{
NSString * editingString = representedObject;
if ([representedObject isKindOfClass: [Token class]])
{
editingString = nil;
}
else
{
editingString = ((WrappedString*)representedObject).text;
}
return editingString;
}
- (id)tokenField:(NSTokenField *)tokenField
representedObjectForEditingString:(NSString *)editingString
{
id repOb = nil;
if ([editingString isEqualToString:@"tok"])
{
repOb = [[[Token alloc] init] autorelease];
}
else
{
WrappedString* wrapped = [[[WrappedString alloc]
init] autorelease];
wrapped.text = editingString;
repOb = wrapped;
}
return repOb;
}
As I'm typing the "hello", none of the delegate methods is called, which seems reasonable. When I type the first "%", there are 3 delegate calls:
tokenField:representedObjectForEditingString:
gets the string "hello" and turns it into aWrappedString
representation.tokenField:styleForRepresentedObject:
gets thatWrappedString
and returnsNSPlainTextTokenStyle
.tokenField:editingStringForRepresentedObject:
gets theWrappedString
and returns "hello".
The first two calls seem reasonable. I'm not sure about number 3, because the token should be editable but it's not being edited yet. I would have thought that tokenField:displayStringForRepresentedObject:
would get called, but it doesn't.
When I type "tok", no delegate methods are called. When I type the second "%", tokenField:representedObjectForEditingString:
receives the string "hellotok", where I would have expected to see just "tok". So I never get a chance to create the rounded token.
If I type the text in the other order, "%tok%hello", then I do get the expected result, a round token followed by plain "hello".
By the way, the Token Field Programming Guide says
Note that there can be only one token per token field that is configured for the plain-text token style.
which seems to imply that it's not possible to freely mix plain text and tokens.