13

I am trying to add a gradient layet to my UILabel for some reasons the CAGradientLayer covers my text. Am I doing anything wrong

- (void)viewDidLoad {
   [super viewDidLoad];

   CAGradientLayer *gradient = [CAGradientLayer layer];
   gradient.frame = CGRectMake(0, 0, myLabel.frame.size.width, myLabel.frame.size.height);
   gradient.colors = myColors;
   [myLabel.layer insertSublayer:gradient atIndex:0];
}
Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358
aryaxt
  • 76,198
  • 92
  • 293
  • 442

3 Answers3

12

The CAGradientLayer is covering the text of your label because the text is drawn by the content of the super layer that you explicitly covered by adding a sublayer.

Your easiest solution is to use two views. A UIView subclass where you override +[UIView layerClass] and return a [CAGradientLayer]. Add a good init method to setup your gradient.

Next pup add the UILabel as a subview of your custom gradient view.

PeyloW
  • 36,742
  • 12
  • 80
  • 99
  • As an aside and out of curiosity about an implementation specific that I accept it would be a very poor idea to rely on even if known; I haven't checked but presumably UILabel uses a CATextLayer as of its availability in iOS 3.2? – Tommy Aug 26 '11 at 20:47
  • @Tommy - `UILabel` is not backed by a `CATextLayer` before or after 3.2. You can easily inspect this with "`po [[someLabel layer] class]`" that will yield a basic `CALayer`. It is also quite visually apparent since `CATextLayer` do not have sub-pixel antialiasing but `UILabel` do. – PeyloW Aug 27 '11 at 08:18
  • Class type isn't always instructive though, as anyone that's ever tried [str isKindOfClass:[NSMutableString class]] can attest. That's a bridging issue and obviously CALayer isn't officially toll-free bridged with anything, but to assume no similar class clustering or aliasing within CALayer is just that: an assumption. I take your point that the sub-pixel antialiasing clue is fairly definitive though. – Tommy Aug 27 '11 at 09:43
  • 1
    Thanks, weird because this works fine on UIButton, but not on UILabel. I would think they both use the same way to draw text. – aryaxt Aug 29 '11 at 05:02
  • 1
    @aryaxt - If you look at the `UIButton.h` header file you will see that the `UIButton` actually has an instance variable named `_titleView`. So for a button view this works simple because the button does not draw it's own text, this is done by the sub label subview. – PeyloW Aug 29 '11 at 08:30
1

I had the same problem. I made this method to get a reference for the shape layer and or generate it if it wasn't there. This method makes sure to throw that layer in the back so that it doesn't cover the label text layer. Just use this and you should be all good, no extra subclasses required.

- (CAShapeLayer*) getShapeLayerForObject: (UIView*) object{
    CAShapeLayer *maskLayer;
    int loc = -1;
    for(int x = 0; x < object.layer.sublayers.count; x++){
        if([[object.layer.sublayers objectAtIndex:x] isKindOfClass:[CAShapeLayer class]]){
            loc = x;
            break;
        }
    }
    if(loc > -1){
        maskLayer = (CAShapeLayer*) [object.layer.sublayers objectAtIndex:loc];
    }else{
        maskLayer = [[CAShapeLayer alloc] init];
        [object.layer insertSublayer:maskLayer atIndex:0];

    }
    return maskLayer;
}
0

If you for example need to subclass your UILabel, then add some CALayer which covers the text, it is recommended to add CATextLayer to your CALayer, here is an swift example:

@IBDesignable class BWRoundedLabel: UILabel {

    override var text: String? {
        didSet {
            updateView()
        }
    }

    @IBInspectable override var shadowColor: UIColor? {

        didSet {
            updateView()
        }
    }

    private func updateView() {

        let width = bounds.size.width - 1
        let height = bounds.size.height - 1

        let shadowLayer = CAShapeLayer()
        shadowLayer.path = UIBezierPath(roundedRect: CGRectMake(0, 0, width, height), cornerRadius: width/2).CGPath
        shadowLayer.fillColor = UIColor.yellowOrange().CGColor
        shadowLayer.shadowColor = shadowColor?.CGColor
        shadowLayer.shadowPath = shadowLayer.path
        shadowLayer.shadowOffset = CGSize(width: 0, height: 1.0)
        shadowLayer.shadowOpacity = 1
        shadowLayer.shadowRadius = 0

        let textLayer = CATextLayer()
        textLayer.foregroundColor = UIColor.whiteColor().CGColor
        textLayer.string = text
        textLayer.fontSize = font.pointSize
        textLayer.font = "Calibri-Bold"
        textLayer.alignmentMode = kCAAlignmentCenter
        textLayer.frame = CGRectMake(0, (height - 16)/2, width, 16)

        if let sublayers = layer.sublayers {
            for sublayer in sublayers {
                sublayer.removeFromSuperlayer()
            }
        }

        layer.insertSublayer(shadowLayer, atIndex: 0)
        layer.insertSublayer(textLayer, atIndex: 1)
    }
}
Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358