iOS UIView 子类,将透明文本绘制到背景

Posted

技术标签:

【中文标题】iOS UIView 子类,将透明文本绘制到背景【英文标题】:iOS UIView subclass, draw see-through text to background 【发布时间】:2013-07-23 17:47:06 【问题描述】:

我想在 UIView 上的子类上绘制文本,以便将文本从形状中剪切出来,并显示视图背后的背景,就像在 here 找到的 OSX Mavericks 徽标中一样。

我想说我更像是一名中级/早期高级 ios 开发人员,所以请随意向我抛出一些疯狂的解决方案。我希望我必须覆盖 drawRect 才能做到这一点。

谢谢大家!

编辑:

我应该提一下,我的第一次尝试是使文本 [UIColor clearColor] 不起作用,因为这只是将文本的 alpha 分量设置为 0,这只是通过文本显示视图。

【问题讨论】:

只是为了澄清您使用的是 Core Text 来绘制而不是 UILabels? 【参考方案1】:

免责声明:我是在未经测试的情况下写的,如果我在这里错了,请原谅我。

你应该通过这两个步骤来实现你所需要的:

    创建一个具有视图大小的CATextLayer,将backgroundColor 设置为完全透明,将foregroundColor 设置为不透明(通过[UIColor colorWithWhite:0 alpha:1][UIColor colorWithWhite:0 alpha:0]。然后设置string属性到您要呈现的字符串,fontfontSize 等。

    将视图的图层蒙版设置为此图层: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),这意味着我们需要与我之前建议的mask 层相反的alpha 值。因此,您需要做的就是使用不透明的颜色进行绘制,这将从您之前在视图上绘制的内容中减去。

【讨论】:

这只会导致用文本掩盖视图,而不是从其中删除文本。图层蒙版使用蒙版图层的不透明度。 如果您反转图层的 alpha,它应该离开视图并剪切文本,不是吗?然后,不要在视图本身上绘制,而是在遮罩层上绘制。 “反转图层的 alpha”不是一回事。如果您将文本图层设置为具有纯色背景颜色和透明文本颜色,则其 alpha 将为填充矩形。 确实如此。我错了,对不起。现在发布了一个可行的解决方案。 像魅力一样工作!谢谢!【参考方案2】:

如果你想要的只是一个白色的视图,剪掉一些东西(文本图像等),那么你可以这样做

yourView.layer.compositingFilter = "screenBlendMode"

这将使白色部分保持白色,黑色部分将透明。

【讨论】:

确实如此。这里的其他答案基本上是“完全错误的”:) 也许不是完全错误但不必要地复杂。感谢您的支持:)【参考方案3】:

实际上,我出人意料地想出了如何自己做,但@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,所以我可以覆盖设置器并调用[self setNeedsDisplay]。不确定这是否完全必要。

我确定这有一些拼写错误,但我可以向你保证,拼写检查版本运行良好。

【讨论】:

【参考方案4】:

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")

【讨论】:

修复此问题并在发布前对其进行测试。

以上是关于iOS UIView 子类,将透明文本绘制到背景的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式在子类 UIView 的子视图上自动布局

iOS - 过滤和转发触摸到子视图

IOS为UIButton添加了阴影

我正在使用 drawRect 实现 UIView 子类:如何绘制按钮?

更新 UIView 子类中 drawRect 中绘制的组件中的文本

iOS:UIView 阻止用户交互,但不是子视图