2012-01-04 15 views
11

Tôi đang tìm cách vẽ một nhãn UILabel (thích hợp hơn thông qua phân lớp) dưới dạng nhãn trong suốt, nhưng với nền chắc chắn. Tôi rút ra một ví dụ nhanh (xin lỗi, nó xấu xí, nhưng nó được các điểm trên :)).drawRect vẽ văn bản 'trong suốt'?

Về cơ bản tôi có UILabel và tôi muốn nền là màu được đặt và văn bản phải trong suốt. Tôi không muốn tô màu văn bản với nền khung nhìn, nhưng thay vào đó nó có màu trong suốt 100%, vì tôi có một kết cấu ở nền mà tôi muốn chắc chắn rằng các đường thẳng lên trong và ngoài nhãn.

Tôi đã bỏ qua đêm duyệt SO và tìm kiếm trên Google, nhưng tôi không tìm thấy nguồn nào hữu ích. Tôi không có nhiều kinh nghiệm với bản vẽ CG, vì vậy tôi sẽ đánh giá cao bất kỳ liên kết, trợ giúp, hướng dẫn hoặc mã mẫu (có lẽ Apple có một số tôi cần phải có một cái nhìn?).

Cảm ơn một nhóm!

enter image description here

+0

Cho đến nay, từ lặn sâu hơn vào các tài liệu thạch anh thú vị, tôi tin rằng tôi cần phải sử dụng 'CGContextSetTextDrawingMode' và 'kCGTextClip' – runmad

+0

Xem câu trả lời của tôi để http://stackoverflow.com/questions/19787238/transparent- uilabel-textcolor-on-superview-superview-sắp xếp-cho một cách tiếp cận dựa trên đường dẫn đến vấn đề này. –

Trả lời

12

Tôi đã viết lại nó như là một lớp con UILabel sử dụng hầu như bất kỳ mã và đăng nó trên GitHub

Các ý chính của nó là bạn ghi đè drawRect nhưng gọi [super drawRect:rect] để cho các UILabel làm như bình thường. Sử dụng màu nhãn trắng cho phép bạn dễ dàng sử dụng chính nhãn đó làm mặt nạ.

- (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

Được giải quyết bằng mặt nạ CALayer. Tạo mặt nạ chuẩn (ví dụ như văn bản được đánh dấu) rất đơn giản. Để tạo ra các văn bản knock-out, tôi đã phải đảo ngược kênh alpha của mặt nạ của tôi, trong đó liên quan đến việc vẽ một nhãn cho một CGImageRef và sau đó làm một số điểm đẩy.

sample mask

Ứng dụng mẫu có sẵn ở đây: https://github.com/robinsenior/RSMaskedLabel

mã có liên quan là ở đây để tránh tương lai liên kết thối:

#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

Dưới đây là một kỹ thuật mà là tương tự như Matt Gallagher, mà sẽ tạo ra một mặt nạ văn bản ngược với một hình ảnh.

Phân bổ bộ đệm dữ liệu (có thể thay đổi). Tạo ngữ cảnh bitmap với kênh alpha 8 bit. Định cấu hình cài đặt cho bản vẽ văn bản. Điền toàn bộ bộ đệm trong chế độ sao chép (màu mặc định được giả định có giá trị alpha là 1). Viết văn bản ở chế độ rõ ràng (giá trị alpha là 0). Tạo một hình ảnh từ ngữ cảnh bitmap. Sử dụng bitmap làm mặt nạ để tạo hình ảnh mới từ hình ảnh nguồn. Tạo UIImage mới và dọn dẹp.

Mỗi lần giá trị textString hoặc sourceImage hoặc kích thước thay đổi, hãy tạo lại hình ảnh cuối cùng.

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

Một cách khác để thực hiện việc này là đưa textImage vào một lớp mới và đặt lớp đó trên lớp của chế độ xem của bạn. (Tháo dòng mà tạo ra "newImage" và "finalImage".) Giả sử điều này xảy ra bên trong xem của bạn đang ở đâu đó:

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; 

Có nhiều lựa chọn thay thế, một số có thể được tốt hơn (phân lớp UIImage và vẽ các văn bản trực tiếp trong rõ ràng chế độ sau khi siêu lớp đã thực hiện bản vẽ của nó?).

+0

Câu trả lời mới của Robin Senior là câu trả lời hay nhất, ngay bây giờ! –