2012-01-04 181 views
11

我正在尋找一個UILabel(最好通過子類化)作爲透明標籤,但具有堅實的背景。我畫了一個快速的例子(對不起,這很難看,但它得到了:))。drawRect繪製'透明'文本?

基本上我有一個UILabel,我想背景是一個設置的顏色,文本應該是透明的。我不想用視圖背景來給文本着色,而是讓它100%透明,因爲我在背景中有一個紋理,我想確保在標籤內部和外部排成一行。

我一直在瀏覽SO並在Google上搜索,但我沒有找到有用的資源。我對CG繪圖沒有太多經驗,所以我會很感激任何鏈接,幫助,教程或示例代碼(也許蘋果有一些我需要看看?)。

非常感謝!

enter image description here

+0

到目前爲止,從潛水進一步進入激動人心的石英文檔,我相信我需要使用'CGContextSetTextDrawingMode'和'kCGTextClip' – runmad 2012-01-04 03:12:00

+0

見我的回答http://stackoverflow.com/questions/19787238/transparent- uilabel -textcolor-on-superview-superview-sort-of爲基於路徑的方法來解決這個問題。 – 2014-02-19 21:27:02

回答

12

我已經重寫它使用幾乎沒有任何代碼一個UILabel子類,並張貼在GitHub

它的要點是你重寫的drawRect但撥打[super drawRect:rect]讓渲染的UILabel正常。使用白色標籤顏色可讓您輕鬆使用標籤本身作爲蒙版。

- (void)drawRect:(CGRect)rect 
{ 
    CGContextRef context = UIGraphicsGetCurrentContext(); 

    // let the superclass draw the label normally 
    [super drawRect:rect]; 

    CGContextConcatCTM(context, CGAffineTransformMake(1, 0, 0, -1, 0, CGRectGetHeight(rect))); 

    // create a mask from the normally rendered text 
    CGImageRef image = CGBitmapContextCreateImage(context); 
    CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(image), CGImageGetHeight(image), CGImageGetBitsPerComponent(image), CGImageGetBitsPerPixel(image), CGImageGetBytesPerRow(image), CGImageGetDataProvider(image), CGImageGetDecode(image), CGImageGetShouldInterpolate(image)); 

    CFRelease(image); image = NULL; 

    // wipe the slate clean 
    CGContextClearRect(context, rect); 

    CGContextSaveGState(context); 
    CGContextClipToMask(context, rect, mask); 

    CFRelease(mask); mask = NULL; 

    [self RS_drawBackgroundInRect:rect]; 

    CGContextRestoreGState(context); 

} 
4

使用的CALayer面具解決。創建標準掩碼(例如,壁紙文本)很簡單。要創建敲出的文本,我不得不反轉我的蒙版的alpha通道,其中包括將標籤渲染到CGImageRef,然後進行一些像素推送。

sample mask

示例應用程序可以在這裏找到:https://github.com/robinsenior/RSMaskedLabel

相關的代碼是在這裏,以避免將來鏈接腐:

#import "RSMaskedLabel.h" 
#import <QuartzCore/QuartzCore.h> 

@interface UIImage (RSAdditions) 
+ (UIImage *) imageWithView:(UIView *)view; 
- (UIImage *) invertAlpha; 
@end 

@interface RSMaskedLabel() 
{ 
    CGImageRef invertedAlphaImage; 
} 
@property (nonatomic, retain) UILabel *knockoutLabel; 
@property (nonatomic, retain) CALayer *textLayer; 
- (void) RS_commonInit; 
@end 

@implementation RSMaskedLabel 
@synthesize knockoutLabel, textLayer; 

- (id)initWithFrame:(CGRect)frame 
{ 
    self = [super initWithFrame:frame]; 
    if (self) 
    { 
     [self RS_commonInit]; 
    } 
    return self; 
} 

- (id)initWithCoder:(NSCoder *)aDecoder 
{ 
    self = [super initWithCoder:aDecoder]; 
    if (self) 
    { 
     [self RS_commonInit]; 
    } 
    return self; 
} 

+ (Class)layerClass 
{ 
    return [CAGradientLayer class]; 
} 

- (void) RS_commonInit 
{ 
    [self setBackgroundColor:[UIColor clearColor]]; 

    // create the UILabel for the text 
    knockoutLabel = [[UILabel alloc] initWithFrame:[self frame]]; 
    [knockoutLabel setText:@"booyah"]; 
    [knockoutLabel setTextAlignment:UITextAlignmentCenter]; 
    [knockoutLabel setFont:[UIFont boldSystemFontOfSize:72.0]]; 
    [knockoutLabel setNumberOfLines:1]; 
    [knockoutLabel setBackgroundColor:[UIColor clearColor]]; 
    [knockoutLabel setTextColor:[UIColor whiteColor]]; 

    // create our filled area (in this case a gradient) 
    NSArray *colors = [[NSArray arrayWithObjects: 
         (id)[[UIColor colorWithRed:0.349 green:0.365 blue:0.376 alpha:1.000] CGColor], 
         (id)[[UIColor colorWithRed:0.455 green:0.490 blue:0.518 alpha:1.000] CGColor], 
         (id)[[UIColor colorWithRed:0.412 green:0.427 blue:0.439 alpha:1.000] CGColor], 
         (id)[[UIColor colorWithRed:0.208 green:0.224 blue:0.235 alpha:1.000] CGColor], 
         nil] retain]; 

    NSArray *gradientLocations = [NSArray arrayWithObjects: 
            [NSNumber numberWithFloat:0.0], 
            [NSNumber numberWithFloat:0.54], 
            [NSNumber numberWithFloat:0.55], 
            [NSNumber numberWithFloat:1], nil]; 

    // render our label to a UIImage 
    // if you remove the call to invertAlpha it will mask the text 
    invertedAlphaImage = [[[UIImage imageWithView:knockoutLabel] invertAlpha] CGImage]; 

    // create a new CALayer to use as the mask 
    textLayer = [CALayer layer]; 
    // stick the image in the layer 
    [textLayer setContents:(id)invertedAlphaImage]; 

    // create a nice gradient layer to use as our fill 
    CAGradientLayer *gradientLayer = (CAGradientLayer *)[self layer]; 

    [gradientLayer setBackgroundColor:[[UIColor clearColor] CGColor]]; 
    [gradientLayer setColors: colors]; 
    [gradientLayer setLocations:gradientLocations]; 
    [gradientLayer setStartPoint:CGPointMake(0.0, 0.0)]; 
    [gradientLayer setEndPoint:CGPointMake(0.0, 1.0)]; 
    [gradientLayer setCornerRadius:10]; 

    // mask the text layer onto our gradient 
    [gradientLayer setMask:textLayer]; 
} 

- (void)layoutSubviews 
{ 
    // resize the text layer 
    [textLayer setFrame:[self bounds]]; 
} 

- (void)dealloc 
{ 
    CGImageRelease(invertedAlphaImage); 
    [knockoutLabel release]; 
    [textLayer  release]; 
    [super   dealloc]; 
} 

@end 

@implementation UIImage (RSAdditions) 

/* 
create a UIImage from a UIView 
*/ 
+ (UIImage *) imageWithView:(UIView *)view 
{ 
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0.0); 
    [view.layer renderInContext:UIGraphicsGetCurrentContext()]; 

    UIImage * img = UIGraphicsGetImageFromCurrentImageContext(); 

    UIGraphicsEndImageContext(); 

    return img; 
} 

/* 
get the image to invert its alpha channel 
*/ 
- (UIImage *)invertAlpha 
{ 
    // scale is needed for retina devices 
    CGFloat scale = [self scale]; 
    CGSize size = self.size; 
    int width = size.width * scale; 
    int height = size.height * scale; 

    CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB(); 

    unsigned char *memoryPool = (unsigned char *)calloc(width*height*4, 1); 

    CGContextRef context = CGBitmapContextCreate(memoryPool, width, height, 8, width * 4, colourSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast); 

    CGColorSpaceRelease(colourSpace); 

    CGContextDrawImage(context, CGRectMake(0, 0, width, height), [self CGImage]); 

    for(int y = 0; y < height; y++) 
    { 
     unsigned char *linePointer = &memoryPool[y * width * 4]; 

     for(int x = 0; x < width; x++) 
     { 
      linePointer[3] = 255-linePointer[3]; 
      linePointer += 4; 
     } 
    } 

    // get a CG image from the context, wrap that into a 
    CGImageRef cgImage = CGBitmapContextCreateImage(context); 
    UIImage *returnImage = [UIImage imageWithCGImage:cgImage scale:scale orientation:UIImageOrientationUp]; 

    // clean up 
    CGImageRelease(cgImage); 
    CGContextRelease(context); 
    free(memoryPool); 

    // and return 
    return returnImage; 
} 
@end 
4

這裏有一個技術,它類似於馬特·加拉格爾的,這將產生帶圖像的倒置文本遮罩。

分配一個(可變的)數據緩衝區。使用8位Alpha通道創建位圖上下文。配置文本繪圖的設置。在複製模式下填充整個緩衝區(默認顏色被假定爲alpha值爲1)。以清晰模式寫入文本(alpha值爲0)。從位圖上下文創建一個圖像。使用位圖作爲掩碼從源圖像創建新圖像。創建一個新的UIImage並清理。

每當textString或sourceImage或size值發生變化時,重新生成最終圖像。

CGSize size = /* assume this exists */; 
UIImage *sourceImage = /* assume this exists */; 
NSString *textString = /* assume this exists */; 
char *text = [textString cStringUsingEncoding:NSMacOSRomanStringEncoding]; 
NSUInteger len = [textString lengthOfBytesUsingEncoding:cStringUsingEncoding:NSMacOSRomanStringEncoding]; 

NSMutableData *data = [NSMutableData dataWithLength:size.width*size.height*1]; 
CGContextRef context = CGBitmapContextCreate([data mutableBytes], size.width, size.height, 8, size.width, NULL, kCGImageAlphaOnly); 

CGContextSelectFont(context, "Gill Sans Bold", 64.0f, kCGEncodingMacRoman); 
CGContextSetTextDrawingMode(context, kCGTextFill); 

CGContextSetBlendMode(context, kCGBlendModeCopy); 
CGContextFillRect(context, overlay.bounds); 
CGContextSetBlendMode(context, kCGBlendModeClear); 
CGContextShowTextAtPoint(context, 16.0f, 16.0f, text, len); 

CGImageRef textImage = CGBitmapContextCreateImage(context); 
CGImageRef newImage = CGImageCreateWithMask(sourceImage.CGImage, textImage); 

UIImage *finalImage = [UIImage imageWithCGImage:newImage]; 

CGContextRelease(context); 
CFRelease(newImage); 
CFRelease(textImage); 

另一種方法是將textImage放入一個新圖層並在視圖的圖層上設置該圖層。 (刪除打造「newImage」和「finalImage」的臺詞。)假設出現這種情況你的看法的地方里面的代碼:

CALayer *maskLayer = [[CALayer alloc] init]; 
CGPoint position = CGPointZero; 

// layout the new layer 
position = overlay.layer.position; 
position.y *= 0.5f; 
maskLayer.bounds = overlay.layer.bounds; 
maskLayer.position = position; 
maskLayer.contents = (__bridge id)textImage; 

self.layer.mask = maskLayer; 

有更多的選擇,有些人可能會更好(子類的UIImage並直接明確繪製文本超類完成繪製後的模式?)。

+0

羅賓高級的新答案現在是最好的答案! – 2013-02-08 18:51:45