使用 Core Graphics 绘制带有减去文本的路径
Posted
技术标签:
【中文标题】使用 Core Graphics 绘制带有减去文本的路径【英文标题】:Drawing a path with subtracted text using Core Graphics 【发布时间】:2013-09-14 00:50:38 【问题描述】:在 Core Graphics 中创建填充路径非常简单,创建填充文本也是如此。但是我还没有找到为子路径中的文本填充的路径示例。我在文本绘制模式、剪辑等方面的实验让我一无所获。
这是一个示例(在 Photoshop 中创建)。您将如何在 Core Graphics 中创建前景形状?
我会提到,这项技术似乎在即将发布的主要移动操作系统版本中大量使用,但我不想与 SO 的 NDA-police 发生冲突;)
【问题讨论】:
请注意,Christian 的回答(目前已被接受)对于任何大小为 128 点或以下的视图都无法正确居中文本。 【参考方案1】:这是我运行和测试的一些代码,它们对你有用。有关详细信息,请参阅内联 cmets:
更新:我删除了manualYOffset:
参数。它现在进行计算以使文本在圆圈中垂直居中。享受吧!
- (void)drawRect:(CGRect)rect
// Make sure the UIView's background is set to clear either in code or in a storyboard/nib
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor whiteColor] setFill];
CGContextAddArc(context, CGRectGetMidX(rect), CGRectGetMidY(rect), CGRectGetWidth(rect)/2, 0, 2*M_PI, YES);
CGContextFillPath(context);
// Manual offset may need to be adjusted depending on the length of the text
[self drawSubtractedText:@"Foo" inRect:rect inContext:context];
- (void)drawSubtractedText:(NSString *)text inRect:(CGRect)rect inContext:(CGContextRef)context
// Save context state to not affect other drawing operations
CGContextSaveGState(context);
// Magic blend mode
CGContextSetBlendMode(context, kCGBlendModeDestinationOut);
// This seemingly random value adjusts the text
// vertically so that it is centered in the circle.
CGFloat Y_OFFSET = -2 * (float)[text length] + 5;
// Context translation for label
CGFloat LABEL_SIDE = CGRectGetWidth(rect);
CGContextTranslateCTM(context, 0, CGRectGetHeight(rect)/2-LABEL_SIDE/2+Y_OFFSET);
// Label to center and adjust font automatically
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, LABEL_SIDE, LABEL_SIDE)];
label.font = [UIFont boldSystemFontOfSize:120];
label.adjustsFontSizeToFitWidth = YES;
label.text = text;
label.textAlignment = NSTextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[label.layer drawInContext:context];
// Restore the state of other drawing operations
CGContextRestoreGState(context);
这是结果(您可以将背景更改为任何内容,您仍然可以看穿文字):
【讨论】:
@ChristianDiLorenzo 的技术确实有效,因此我已将答案授予他,但是我最终使用了一种不同的技术,将字符串转换为 CGPathRef,然后使用奇偶绘图从外部路径(它不像 Christian 的建议那样处理部分重叠,但它更容易在 ios 和 OSX 之间移植)。感谢 Christian,对于延迟接受表示歉意。 注意:这只适用于较大的磅值。如果您有 128 点或更少的方形视图,则文本不会居中。 对于那些正在寻找一种快速“通过”已经绘制的东西的方法的人来说,使用CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeDestinationOut)
然后绘制带有 any 属性的 any 文本使用当前图形上下文或-[UIBezierPath fill]
和朋友。【参考方案2】:
下面是一个UIView
子类,它会做你想做的事。它将正确地调整圆圈中 1 个或多个字母的大小和位置。以下是 1-3 个不同大小的字母(32、64、128、256)的外观:
借助 Interface Builder 中用户定义的运行时属性的可用性,您甚至可以在 IB 中配置视图。只需将text
属性设置为运行时属性,将backgroundColor
设置为您想要的圆圈颜色。
代码如下:
@interface MELetterCircleView : UIView
/**
* The text to display in the view. This should be limited to
* just a few characters.
*/
@property (nonatomic, strong) NSString *text;
@end
@interface MELetterCircleView ()
@property (nonatomic, strong) UIColor *circleColor;
@end
@implementation MELetterCircleView
- (instancetype)initWithFrame:(CGRect)frame text:(NSString *)text
NSParameterAssert(text);
self = [super initWithFrame:frame];
if (self)
self.text = text;
return self;
// Override to set the circle's background color.
// The view's background will always be clear.
-(void)setBackgroundColor:(UIColor *)backgroundColor
self.circleColor = backgroundColor;
[super setBackgroundColor:[UIColor clearColor]];
- (void)drawRect:(CGRect)rect
CGContextRef context = UIGraphicsGetCurrentContext();
[self.circleColor setFill];
CGContextAddArc(context, CGRectGetMidX(rect), CGRectGetMidY(rect),
CGRectGetWidth(rect)/2, 0, 2*M_PI, YES);
CGContextFillPath(context);
[self drawSubtractedText:self.text inRect:rect inContext:context];
- (void)drawSubtractedText:(NSString *)text inRect:(CGRect)rect
inContext:(CGContextRef)context
CGContextSaveGState(context);
// Magic blend mode
CGContextSetBlendMode(context, kCGBlendModeDestinationOut);
CGFloat pointSize =
[self optimumFontSizeForFont:[UIFont boldSystemFontOfSize:100.f]
inRect:rect
withText:text];
UIFont *font = [UIFont boldSystemFontOfSize:pointSize];
// Move drawing start point for centering label.
CGContextTranslateCTM(context, 0,
(CGRectGetMidY(rect) - (font.lineHeight/2)));
CGRect frame = CGRectMake(0, 0, CGRectGetWidth(rect), font.lineHeight)];
UILabel *label = [[UILabel alloc] initWithFrame:frame];
label.font = font;
label.text = text;
label.textAlignment = NSTextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[label.layer drawInContext:context];
// Restore the state of other drawing operations
CGContextRestoreGState(context);
-(CGFloat)optimumFontSizeForFont:(UIFont *)font inRect:(CGRect)rect
withText:(NSString *)text
// For current font point size, calculate points per pixel
CGFloat pointsPerPixel = font.lineHeight / font.pointSize;
// Scale up point size for the height of the label.
// This represents the optimum size of a single letter.
CGFloat desiredPointSize = rect.size.height * pointsPerPixel;
if ([text length] == 1)
// In the case of a single letter, we need to scale back a bit
// to take into account the circle curve.
// We could calculate the inner square of the circle,
// but this is a good approximation.
desiredPointSize = .80*desiredPointSize;
else
// More than a single letter. Let's make room for more.
desiredPointSize = desiredPointSize / [text length];
return desiredPointSize;
@end
【讨论】:
以上是关于使用 Core Graphics 绘制带有减去文本的路径的主要内容,如果未能解决你的问题,请参考以下文章