如何在 iOS 中为类似标签的文本输入创建文本字段?
How do I create a Textfield for Tag-Like-Text Input in iOS?
我正在创建一个应用程序,用户可以通过按几个按钮来输入文本(单词或短语)。
文本被插入到 UITextField 中,用户可以在其中输入自己的附加文本。为了简化与已插入文本的交互,我希望有一种行为类似于标准消息应用程序在搜索新对话的联系人时的行为方式,或者 Evernote 处理标签的方式。
也就是说,插入后,单词或词组只能作为一个整体选择,按键盘上的删除键会先选择整个"Tag",然后在下一次按下时将其删除。
有没有办法使标准 UITextField 的行为像这样,或者有 UITextField 的开源实现吗?
我的 OS X 应用程序需要类似的东西。他们在那里使用了一种叫做 NSTokenField
的东西,但不幸的是,在 iOS 平台上没有等效的东西。所以你基本上只能创建自己的令牌或使用现有的第三方库。如果您需要有关如何创建自定义令牌的详细信息,请告诉我。
P.S。创建自定义标记需要重写 drawRect
方法和贝塞尔曲线路径的一些用法。
扩展答案:
我将向您展示如何在 NSTextView
上创建标记(其中包含文本,但也可以包含 NSTextAttachment
。此外,代码适用于 Cocoa(但只需稍加调整即可实现相同的效果在Cocoa触摸
1.) 创建扩展 NSTextAttachmentCell
的 class
2.) 覆盖方法-(void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
[self drawWithFrame:cellFrame inView:controlView characterIndex:NSNotFound layoutManager:nil];
}
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView characterIndex:(NSUInteger)charIndex layoutManager:(NSLayoutManager *)layoutManager
的实现是这样的:
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView characterIndex:(NSUInteger)charIndex layoutManager:(NSLayoutManager *)layoutManager
{
NSColor* bgColor = [NSColor redColor];
NSColor* borderColor = [NSColor blackColor];
NSRect frame = cellFrame;
CGFloat radius = ceilf([self cellSize].height / 2.f);
NSBezierPath* roundedRectanglePath = [NSBezierPath bezierPathWithRoundedRect: NSMakeRect(NSMinX(frame) + 0.5, NSMinY(frame) + 3.5, NSWidth(frame) - 1, NSHeight(frame) - 1) xRadius: radius yRadius: radius];
[bgColor setFill];
[roundedRectanglePath fill];
[borderColor setStroke];
[roundedRectanglePath setLineWidth: 1];
[roundedRectanglePath stroke];
CGSize size = [[self stringValue] sizeWithAttributes:@{NSFontAttributeName:defaultFont}];
CGRect textFrame = CGRectMake(cellFrame.origin.x + (cellFrame.size.width - size.width)/2,
cellFrame.origin.y + 2.f,
size.width,
size.height);
[[self stringValue] drawInRect:textFrame withAttributes:@{NSFontAttributeName:defaultFont, NSForegroundColorAttributeName: [NSColor whiteColor]}];
}
所有代码都应该是自解释的,但如果您有任何问题,请提出。希望对你有帮助。
我需要在 OS X 上进行更多混合,允许混合类似 Facebook 的标签和普通文本。所以我构建了自己的实现 https://github.com/aiman86/ANTaggedTextView,它只处理视图渲染,而不处理自动完成或选择(计划很快实现后者):
继承 NSTextView 并覆盖 drawRect
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
[self.attributedString enumerateAttributesInRange:(NSRange){0, self.string.length} options:NSAttributedStringEnumerationReverse usingBlock:
^(NSDictionary *attributes, NSRange range, BOOL *stop) {
if ([attributes objectForKey:@"Tag"] != nil)
{
NSDictionary* tagAttributes = [self.attributedString attributesAtIndex:range.location effectiveRange:nil];
NSSize oneCharSize = [@"a" sizeWithAttributes:tagAttributes];
NSRange activeRange = [self.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL];
NSRect tagRect = [self.layoutManager boundingRectForGlyphRange:activeRange inTextContainer:self.textContainer];
tagRect.origin.x += self.textContainerOrigin.x;
tagRect.origin.y += self.textContainerOrigin.y;
tagRect = [self convertRectToLayer:tagRect];
NSRect tagBorderRect = (NSRect){ (NSPoint){tagRect.origin.x-oneCharSize.width*0.25, tagRect.origin.y+1}, (NSSize){tagRect.size.width+oneCharSize.width*0.5, tagRect.size.height} };
[NSGraphicsContext saveGraphicsState];
NSBezierPath* path = [NSBezierPath bezierPathWithRoundedRect:tagBorderRect xRadius:3.0f yRadius:3.0f];
NSColor* fillColor = [NSColor colorWithCalibratedRed:237.0/255.0 green:243.0/255.0 blue:252.0/255.0 alpha:1];
NSColor* strokeColor = [NSColor colorWithCalibratedRed:163.0/255.0 green:188.0/255.0 blue:234.0/255.0 alpha:1];
NSColor* textColor = [NSColor colorWithCalibratedRed:37.0/255.0 green:62.0/255.0 blue:112.0/255.0 alpha:1];
[path addClip];
[fillColor setFill];
[strokeColor setStroke];
NSRectFillUsingOperation(tagBorderRect, NSCompositeSourceOver);
NSAffineTransform *transform = [NSAffineTransform transform];
[transform translateXBy: 0.5 yBy: 0.5];
[path transformUsingAffineTransform: transform];
[path stroke];
[transform translateXBy: -1.5 yBy: -1.5];
[path transformUsingAffineTransform: transform];
[path stroke];
NSMutableDictionary* attrs = [NSMutableDictionary dictionaryWithDictionary:tagAttributes];
NSFont* font = [tagAttributes valueForKey:NSFontAttributeName];
font = [[NSFontManager sharedFontManager] convertFont:font toSize:[font pointSize] - 0.25];
[attrs addEntriesFromDictionary:@{NSFontAttributeName: font, NSForegroundColorAttributeName: textColor}];
[[self.attributedString.string substringWithRange:range] drawInRect:tagRect withAttributes:attrs];
[NSGraphicsContext restoreGraphicsState];
}
}];
}
- 现在您可以简单地在 NSAttributedString 中使用属性 "Tag"(具有任何值),文本视图将以类似于 Facebook 标签的外观呈现标签文本。
我在 github 的实现还提供了 NSTextField 和 NSTextFieldCell 实现,如果您有兴趣在 table 视图中使用它:
希望对处于类似情况的人有所帮助,如果您发现不足之处,请随时发表评论,我是 Cocoa 应用程序开发的新手。
我正在创建一个应用程序,用户可以通过按几个按钮来输入文本(单词或短语)。
文本被插入到 UITextField 中,用户可以在其中输入自己的附加文本。为了简化与已插入文本的交互,我希望有一种行为类似于标准消息应用程序在搜索新对话的联系人时的行为方式,或者 Evernote 处理标签的方式。
也就是说,插入后,单词或词组只能作为一个整体选择,按键盘上的删除键会先选择整个"Tag",然后在下一次按下时将其删除。
有没有办法使标准 UITextField 的行为像这样,或者有 UITextField 的开源实现吗?
我的 OS X 应用程序需要类似的东西。他们在那里使用了一种叫做 NSTokenField
的东西,但不幸的是,在 iOS 平台上没有等效的东西。所以你基本上只能创建自己的令牌或使用现有的第三方库。如果您需要有关如何创建自定义令牌的详细信息,请告诉我。
P.S。创建自定义标记需要重写 drawRect
方法和贝塞尔曲线路径的一些用法。
扩展答案:
我将向您展示如何在 NSTextView
上创建标记(其中包含文本,但也可以包含 NSTextAttachment
。此外,代码适用于 Cocoa(但只需稍加调整即可实现相同的效果在Cocoa触摸
1.) 创建扩展 NSTextAttachmentCell
2.) 覆盖方法-(void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
{
[self drawWithFrame:cellFrame inView:controlView characterIndex:NSNotFound layoutManager:nil];
}
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView characterIndex:(NSUInteger)charIndex layoutManager:(NSLayoutManager *)layoutManager
的实现是这样的:
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView characterIndex:(NSUInteger)charIndex layoutManager:(NSLayoutManager *)layoutManager
{
NSColor* bgColor = [NSColor redColor];
NSColor* borderColor = [NSColor blackColor];
NSRect frame = cellFrame;
CGFloat radius = ceilf([self cellSize].height / 2.f);
NSBezierPath* roundedRectanglePath = [NSBezierPath bezierPathWithRoundedRect: NSMakeRect(NSMinX(frame) + 0.5, NSMinY(frame) + 3.5, NSWidth(frame) - 1, NSHeight(frame) - 1) xRadius: radius yRadius: radius];
[bgColor setFill];
[roundedRectanglePath fill];
[borderColor setStroke];
[roundedRectanglePath setLineWidth: 1];
[roundedRectanglePath stroke];
CGSize size = [[self stringValue] sizeWithAttributes:@{NSFontAttributeName:defaultFont}];
CGRect textFrame = CGRectMake(cellFrame.origin.x + (cellFrame.size.width - size.width)/2,
cellFrame.origin.y + 2.f,
size.width,
size.height);
[[self stringValue] drawInRect:textFrame withAttributes:@{NSFontAttributeName:defaultFont, NSForegroundColorAttributeName: [NSColor whiteColor]}];
}
所有代码都应该是自解释的,但如果您有任何问题,请提出。希望对你有帮助。
我需要在 OS X 上进行更多混合,允许混合类似 Facebook 的标签和普通文本。所以我构建了自己的实现 https://github.com/aiman86/ANTaggedTextView,它只处理视图渲染,而不处理自动完成或选择(计划很快实现后者):
继承 NSTextView 并覆盖 drawRect
- (void)drawRect:(NSRect)dirtyRect { [super drawRect:dirtyRect]; [self.attributedString enumerateAttributesInRange:(NSRange){0, self.string.length} options:NSAttributedStringEnumerationReverse usingBlock: ^(NSDictionary *attributes, NSRange range, BOOL *stop) { if ([attributes objectForKey:@"Tag"] != nil) { NSDictionary* tagAttributes = [self.attributedString attributesAtIndex:range.location effectiveRange:nil]; NSSize oneCharSize = [@"a" sizeWithAttributes:tagAttributes]; NSRange activeRange = [self.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL]; NSRect tagRect = [self.layoutManager boundingRectForGlyphRange:activeRange inTextContainer:self.textContainer]; tagRect.origin.x += self.textContainerOrigin.x; tagRect.origin.y += self.textContainerOrigin.y; tagRect = [self convertRectToLayer:tagRect]; NSRect tagBorderRect = (NSRect){ (NSPoint){tagRect.origin.x-oneCharSize.width*0.25, tagRect.origin.y+1}, (NSSize){tagRect.size.width+oneCharSize.width*0.5, tagRect.size.height} }; [NSGraphicsContext saveGraphicsState]; NSBezierPath* path = [NSBezierPath bezierPathWithRoundedRect:tagBorderRect xRadius:3.0f yRadius:3.0f]; NSColor* fillColor = [NSColor colorWithCalibratedRed:237.0/255.0 green:243.0/255.0 blue:252.0/255.0 alpha:1]; NSColor* strokeColor = [NSColor colorWithCalibratedRed:163.0/255.0 green:188.0/255.0 blue:234.0/255.0 alpha:1]; NSColor* textColor = [NSColor colorWithCalibratedRed:37.0/255.0 green:62.0/255.0 blue:112.0/255.0 alpha:1]; [path addClip]; [fillColor setFill]; [strokeColor setStroke]; NSRectFillUsingOperation(tagBorderRect, NSCompositeSourceOver); NSAffineTransform *transform = [NSAffineTransform transform]; [transform translateXBy: 0.5 yBy: 0.5]; [path transformUsingAffineTransform: transform]; [path stroke]; [transform translateXBy: -1.5 yBy: -1.5]; [path transformUsingAffineTransform: transform]; [path stroke]; NSMutableDictionary* attrs = [NSMutableDictionary dictionaryWithDictionary:tagAttributes]; NSFont* font = [tagAttributes valueForKey:NSFontAttributeName]; font = [[NSFontManager sharedFontManager] convertFont:font toSize:[font pointSize] - 0.25]; [attrs addEntriesFromDictionary:@{NSFontAttributeName: font, NSForegroundColorAttributeName: textColor}]; [[self.attributedString.string substringWithRange:range] drawInRect:tagRect withAttributes:attrs]; [NSGraphicsContext restoreGraphicsState]; } }]; }
- 现在您可以简单地在 NSAttributedString 中使用属性 "Tag"(具有任何值),文本视图将以类似于 Facebook 标签的外观呈现标签文本。
我在 github 的实现还提供了 NSTextField 和 NSTextFieldCell 实现,如果您有兴趣在 table 视图中使用它:
希望对处于类似情况的人有所帮助,如果您发现不足之处,请随时发表评论,我是 Cocoa 应用程序开发的新手。