Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transparent UILabel textColor on superview.superview (sort of)

I would like to achieve the same effect as in the new iTunes remote update for iOS 7 for my UILabel.

If you look at this screen:

http://313e5987718b346aaf83-f5e825270f29a84f7881423410384342.r78.cf1.rackcdn.com/1383617182-2013-11-05_03.05.58_04.png

(The one on the right) you'll notice that the UILabel text color is the background blurred album cover without the black mask.

At the moment I have transparent UILabel textColor (http://cl.ly/SInK), my code is similar to https://github.com/robinsenior/RSMaskedLabel

My first assumption is to have a view hierarchy like that

UIImageView (Light blurred image)
    |
UIImageView/UIView (Dark blurred image or just a dark mask for the superview)
    |
UILabel (And all my other view). 

I would like the UILabel to have a transparent text color on the first UIImageView, ignoring the second one/Mask).

I can't wrap my head around a solution to achieve this effect.

like image 930
Dimillian Avatar asked Nov 05 '13 10:11

Dimillian


2 Answers

Since iOS 8 Apple offer a new class, UIVibrancyEffect, which can be added to a UIVisualEffectView. This combined with a UIBlurEffect can achieve the exact same result as my screenshot above.

Source: https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIVibrancyEffect/index.html

like image 85
Dimillian Avatar answered Oct 20 '22 20:10

Dimillian


I don't think you're going to be able to directly do this with a UILabel. What I did was decompose it into several pieces:

How to draw only the background of a label. For this I cribbed code from CGPathRef from string to turn the string into a path. Then the subclassing UILabel and adding the following drawRect did the appropriate thing.

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

    // Convert the attributed text to a UIBezierPath
    CGPathRef           path = [self.attributedText createPath];

    // Adjust the path bounds horizontally as requested by the view
    CGRect              bounds = [self textRectForBounds:rect limitedToNumberOfLines:self.numberOfLines];
    CGAffineTransform   xform = CGAffineTransformMakeTranslation(bounds.origin.x, bounds.origin.y);

    // Adjust the path bounds vertically because it doesn't seem to be taken into account yet
    bounds = CGPathGetBoundingBox(path);
    xform = CGAffineTransformTranslate(xform, 0., (self.bounds.size.height - bounds.size.height) / 2.);

    // Apply the transform to the path
    path = CGPathCreateCopyByTransformingPath(path, &xform);

    // Set colors, the fill color should be the background color because we're going
    //  to draw everything BUT the text, the text will be left clear.
    CGContextSetFillColorWithColor(ctx, self.textColor.CGColor);

    // Flip and offset things
    CGContextScaleCTM(ctx, 1., -1.);
    CGContextTranslateCTM(ctx, 0., 0. - self.bounds.size.height);

    // Invert the path
    CGContextAddRect(ctx, self.bounds);
    CGContextAddPath(ctx, path);
    CGContextDrawPath(ctx, kCGPathEOFill);

    // Discard the path
    CFRelease(path);

    // Restore gstate
    CGContextRestoreGState(ctx);
}

If you create a UILabel in interface builder, set the class, set the background color to clear and the foreground color to your proposed fill color, the background will show through where the text is.

To blur the body showing through the label, I dropped an FXBlurView (https://github.com/nicklockwood/FXBlurView) under the label with sizing constraints to keep them aligned.

You can see it all working here https://github.com/dwb357/TransparentLabel.

like image 20
David Berry Avatar answered Oct 20 '22 21:10

David Berry