13

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

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

多谢你们!

编辑:

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

4

4 回答 4

18

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

您应该通过以下两个步骤实现您所需要的:

  1. 创建一个CATextLayer与您的视图大小相同,将 设置backgroundColor为完全透明和foregroundColor不透明(通过[UIColor colorWithWhite:0 alpha:1][UIColor colorWithWhite:0 alpha:0]。然后将string属性设置为您要渲染的字符串,font等等fontSize

  2. 将视图的图层蒙版设置为此图层: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因此,您需要做的就是使用不透明的颜色进行绘制,这将从您事先在视图上绘制的内容中减去。

于 2013-07-23T18:15:16.723 回答
3

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

yourView.layer.compositingFilter = "screenBlendMode"

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

于 2019-03-11T12:57:52.410 回答
1

实际上,我出人意料地想出了如何自己做,但是@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]. 不确定这是否完全必要。

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

于 2013-07-24T14:30:40.917 回答
1

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")
于 2018-11-01T19:43:08.127 回答