5

I have an UIView with rounded corners and drop shadow, implemented and working. But the UIView has a boring background color, white. So I want to put a gradient layer as the background. Below the labels, buttons and most important, make it so the rounded corners still appears.

CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = subHudView.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], (id)[[UIColor whiteColor] CGColor], nil];
[subHudView.layer addSublayer:gradient];
subHudView.layer.cornerRadius = 8;
subHudView.layer.masksToBounds = NO;
subHudView.layer.shadowOffset = CGSizeMake(-5, 5);
subHudView.layer.shadowRadius = 8;
subHudView.layer.shadowOpacity = 0.75;

This is my code as I tried to implement it, but the gradient layer is on top of everything in the view know. How can I make the gradient go under all the controls and labels? Every responding help will be appreciated.

Jacob
  • 1,310
  • 1
  • 15
  • 29

5 Answers5

11

in Swift:

let gradientLayer = CAGradientLayer()
gradientLayer.cornerRadius = 20.0
ytp92
  • 992
  • 1
  • 9
  • 8
2

A layer added to your view (addSublayer) is drawn in front of your view's own layer, which is where all your view's drawing takes place. What you want is to draw the gradient into your view's own layer. To do so, implement +layerClass in your view, specifying that you want your view's layer to be a gradient view.

So, for example (in the view's own code):

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

-(void)awakeFromNib {
    [super awakeFromNib];
    CAGradientLayer* layer = (CAGradientLayer*)self.layer;
    layer.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], (id)[[UIColor whiteColor] CGColor], nil];
    layer.cornerRadius = 8;
    layer.masksToBounds = NO;
    layer.shadowOffset = CGSizeMake(-5, 5);
    layer.shadowRadius = 8;
    layer.shadowOpacity = 0.75;
}

If you then implement drawRect:, however, you'll of course wipe out your gradient and your rounded corners. There are ways around that...

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Could you please show me an example on how to implement the `+layerClass`? – Jacob Nov 25 '11 at 19:54
  • Okay, thanks! But `self.layer` gives me an error. It's not declared in my header file. How is it suppose to be? – Jacob Nov 27 '11 at 14:14
  • Remember, I said that that code needs to be in the view's own code. You will need to create a UIView subclass in order to have a place to put it. – matt Nov 27 '11 at 15:07
  • But why is the method @gcamp posted wrong? Is there any downside using it? – Jacob Nov 27 '11 at 15:10
  • @Jacos - The method isn't wrong. What he said is wrong. A view has its own primary layer and that layer can have subviews. You can draw the rounded rect, the shadow, and the gradient in the view's own layer, which is what I showed you how to do; or you can add a sublayer and draw the rounded rect, the shadow, and the gradient in that sublayer, as you are apparently now doing. It doesn't matter as long you *understand* what you're doing. His statement that you needed to use `insertSublayer:atIndex:` would tend to mar any such understanding. – matt Nov 28 '11 at 17:00
  • Thanks for clearing things out. I do understand and think I achieved the desired effect anyway, thanks. Will kind of promote my other question here... Would need an explanation to this: [link]http://stackoverflow.com/questions/8282898/quartzcore-shadow-lag-uitableview – Jacob Nov 28 '11 at 17:58
  • Yes, well, that really is a different topic. Everything you're doing here (using the layer to get rounded corners, gradient, shadow) is inefficient drawing. I was going to mention this, except that in your particular use in this question it doesn't matter. There's a good WWDC 2011 video on this topic (and 2010 too). – matt Nov 29 '11 at 03:04
1

When you addSublayer: it add the layer at the top of all the sublayers.

You should probably use something like that instead :

[subHudView.layer insertSublayer:gradient atIndex:0];

That way, the CAGradientLayer will be below everything else.

gcamp
  • 14,622
  • 4
  • 54
  • 85
  • Thanks, I've tried this before as well. But when I implement it the rounded corners disappear. Any thoughts on that? **Edit** I got it to work, just had to make the gradient have rounded corners too. – Jacob Nov 25 '11 at 19:56
  • This is wrong. The gradient layer is still in front of your view's layer. It has to be: a sublayer is always is front of its superlayer. @Jacos might be able to make the new layer appear the way he wants, but he will not have made the gradient layer the actual background to his view, as he asked to do. – matt Nov 25 '11 at 20:06
  • Well, you might have a better answer (up voted), but this is not wrong. He wanted the gradient layer to go below the other label and controls, and my answer does what he asked. – gcamp Nov 25 '11 at 20:12
  • It *is* wrong. You said to say `insertSublayer:atIndex:`. That makes no difference whatever, and is just misleading to the OP. He was rounding the corners of the view's layer. Your code still puts the gradient layer in front of that. – matt Nov 25 '11 at 20:27
0

As well as the insertSublayer:atIndex: that gcamp suggested, you can also use insertSublayer:above: and insertSublayer:below: to place your layer in specific places.

[self.view.layer insertSublayer:gradient below:someUIObject];
eric.mitchell
  • 8,817
  • 12
  • 54
  • 92
0

Rather than create a new layer, override your view's layer method:

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

This will ensure that your view create a CAGradientLayer, rather than a basic CALayer, as the base layer.

Then, during init get hold of the layer:

CAGradientLayer *gradLayer = self.layer;
gradLayer.colors = [NSArray arrayWithObjects:(id)[[UIColor blackColor] CGColor], (id)[[UIColor whiteColor] CGColor], nil];

and assign your gradients to it.

Nice and clean...

tarmes
  • 15,366
  • 10
  • 53
  • 87