iosswiftuibuttoncalayer

Transparent UIButton title


How can I subtract the shape of the title text from the button background?

enter image description here

I created a custom UIButton class. Currently the code to add the border and text color looks simply like this

layer.borderWidth = 2.0
layer.borderColor = buttonColor.CGColor
layer.cornerRadius = (CGFloat(bounds.height) + paddingVertical * 2.0) / 2.0
setTitleColor(buttonColor, forState: UIControlState.Normal)

Any advice on how I can subtract the button title shape from background. Do I need to render all this as an image or is there a better alternative?


Solution

  • I've written some code for you, my UIButton subclass called AKStencilButton (available on github https://github.com/purrrminator/AKStencilButton):

    #import "AKStencilButton.h"
    #import <QuartzCore/QuartzCore.h>
    
    @interface AKStencilButton ()
    {
        UIColor * _buttonColor;
    }
    @end
    
    @implementation AKStencilButton
    
    -(id)initWithCoder:(NSCoder *)aDecoder
    {
        if (self = [super initWithCoder:aDecoder]){
            [self setupDefaults];
        }
        return self;
    }
    -(instancetype)initWithFrame:(CGRect)frame
    {
        if (self = [super initWithFrame:frame]){
            [self setupDefaults];
        }
        return self;
    }
    -(void)setupDefaults
    {
        self.backgroundColor = [UIColor redColor];
        self.layer.cornerRadius = 4;
        self.clipsToBounds = YES;
    }
    -(void)layoutSubviews
    {
        [super layoutSubviews];
        [self refreshMask];
    }
    -(void)setTitleColor:(UIColor *)color forState:(UIControlState)state
    {
        [super setTitleColor:[UIColor clearColor] forState:state];
    }
    -(void)refreshMask
    {
        self.titleLabel.backgroundColor = [UIColor clearColor];
        [self setTitleColor:[UIColor clearColor] forState:UIControlStateNormal];
    
        NSString * text = self.titleLabel.text;
        CGSize buttonSize = self.bounds.size;
        UIFont * font = self.titleLabel.font;
    
        NSDictionary* attribs = @{NSFontAttributeName: self.titleLabel.font};
        CGSize textSize = [text sizeWithAttributes:attribs];
    
    
        UIGraphicsBeginImageContextWithOptions(buttonSize, NO, [[UIScreen mainScreen] scale]);
        CGContextRef ctx = UIGraphicsGetCurrentContext();
        CGContextSetFillColorWithColor(ctx, [UIColor whiteColor].CGColor);
        CGPoint center = CGPointMake(buttonSize.width/2-textSize.width/2, buttonSize.height/2-textSize.height/2);
    
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, buttonSize.width, buttonSize.height)];
        CGContextAddPath(ctx, path.CGPath);
        CGContextFillPath(ctx);
        CGContextSetBlendMode(ctx, kCGBlendModeDestinationOut);
    
        [text drawAtPoint:center withAttributes:@{NSFontAttributeName:font}];
        UIImage* viewImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
        CALayer *maskLayer = [CALayer layer];
        maskLayer.contents = (__bridge id)(viewImage.CGImage);
        maskLayer.frame = self.bounds;
        self.layer.mask = maskLayer;
    }
    @end
    

    it looks like this (sorry for the rainbow):

    enter image description here