我想在 UIView 上的子类上绘制文本,以便将文本从形状中剪切出来,并显示视图后面的背景,就像在此处找到的 OSX Mavericks 徽标中一样。
我会说我更像是一名中级/早期高级 iOS 开发人员,所以请随意向我抛出一些疯狂的解决方案。我希望我必须重写drawRect
才能做到这一点。
多谢你们!
编辑:
我应该提一下,我的第一次尝试是制作[UIColor clearColor]
不起作用的文本,因为这只是将文本的 alpha 分量设置为 0,这只是通过文本显示视图。
我想在 UIView 上的子类上绘制文本,以便将文本从形状中剪切出来,并显示视图后面的背景,就像在此处找到的 OSX Mavericks 徽标中一样。
我会说我更像是一名中级/早期高级 iOS 开发人员,所以请随意向我抛出一些疯狂的解决方案。我希望我必须重写drawRect
才能做到这一点。
多谢你们!
编辑:
我应该提一下,我的第一次尝试是制作[UIColor clearColor]
不起作用的文本,因为这只是将文本的 alpha 分量设置为 0,这只是通过文本显示视图。
免责声明:我在没有测试的情况下写这个,如果我在这里错了,请原谅我。
您应该通过以下两个步骤实现您所需要的:
创建一个CATextLayer
与您的视图大小相同,将 设置backgroundColor
为完全透明和foregroundColor
不透明(通过[UIColor colorWithWhite:0 alpha:1]
和[UIColor colorWithWhite:0 alpha:0]
。然后将string
属性设置为您要渲染的字符串,font
等等fontSize
。
将视图的图层蒙版设置为此图层:myView.layer.mask = textLayer
. 您必须导入QuartzCore
才能访问CALayer
您的视图。
请注意,我可能在第一步中在不透明和透明颜色之间进行了切换。
编辑:的确,诺亚是对的。为了克服这个问题,我将 CoreGraphics 与kCGBlendModeDestinationOut
混合模式一起使用。
首先,显示它确实有效的示例视图:
@implementation TestView
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor clearColor];
}
return self;
}
- (void)drawRect:(CGRect)rect {
[[UIColor redColor] setFill];
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:10];
[path fill];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context); {
CGContextSetBlendMode(context, kCGBlendModeDestinationOut);
[@"Hello!" drawAtPoint:CGPointZero withFont:[UIFont systemFontOfSize:24]];
} CGContextRestoreGState(context);
}
@end
将此添加到您的视图控制器后,您将看到绘制TestView
位置后面的视图。Hello!
为什么会这样:
混合模式定义为,这意味着我们需要与我之前建议的图层R = D*(1 - Sa)
相反的 alpha 值。mask
因此,您需要做的就是使用不透明的颜色进行绘制,这将从您事先在视图上绘制的内容中减去。
如果你想要的只是一个白色视图,其中一些东西(文本图像等)被剪掉了,那么你可以做
yourView.layer.compositingFilter = "screenBlendMode"
这将使白色部分保持白色,黑色部分将透明。
实际上,我出人意料地想出了如何自己做,但是@StatusReport 的答案是完全有效的,并且可以按现在的方式工作。
我是这样做的:
-(void)drawRect:(CGRect)rect{
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor darkGrayColor]setFill]; //this becomes the color of the alpha mask
CGContextFillRect(context, rect);
CGContextSaveState(context);
[[UIColor whiteColor]setFill];
//Have to flip the context since core graphics is done in cartesian coordinates
CGContextTranslateCTM(context, 0, rect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
[textToDraw drawInRect:rect withFont:[UIFont fontWithName:@"HelveticaNeue-Thin" size:40];
CGContextRestoreGState(context);
CGImageRef alphaMask = CGBitmapContextCreateImage(context);
[[UIColor whiteColor]setFill];
CGContextFillRect(context, rect);
CGContextSaveGState(context);
CGContentClipToMask(context, rect, alphaMask);
[backgroundImage drawInRect:rect];
CGContextRestoreGState(context);
CGImageRelease(alphaMask);
}
- (void)setTextToDraw:(NSString*)text{
if(text != textToDraw){
textToDraw = text;
[self setNeedsDisplay];
}
}
我有一个@synthesize
声明,textToDraw
所以我可以覆盖 setter 并调用[self setNeedsDisplay]
. 不确定这是否完全必要。
我敢肯定这有一些拼写错误,但我可以向你保证,拼写检查版本运行良好。
StatusReport 接受的答案写得很漂亮,因为我还没有找到这个问题的 Swift 答案,所以我想我会使用他的答案作为 Swift 版本的模板。我通过允许输入字符串作为视图的参数来为视图添加一点可扩展性,以提醒人们他可以使用视图的所有属性(角半径、颜色等)来完成视图完全可扩展的。
框架在初始化程序中被清零,因为您很可能会应用约束。如果您没有使用约束,请忽略它。
斯威夫特 5
class CustomView: UIView {
var title: String
init(title: String) {
self.title = title
super.init(frame: CGRect.zero)
config()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func config() {
backgroundColor = UIColor.clear
}
override func draw(_ rect: CGRect) { // do not call super
UIColor.red.setFill()
let path = UIBezierPath(roundedRect: bounds, cornerRadius: 10)
path.fill()
weak var context = UIGraphicsGetCurrentContext() // Apple wants this to be weak
context?.saveGState()
context?.setBlendMode(CGBlendMode.destinationOut)
title.draw(at: CGPoint.zero, withAttributes: [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 24)])
context?.restoreGState()
}
}
let customView = CustomView(title: "great success")