0

I've created a CAGradientLayer, which I'd like to add as a sublayer to a UIView. I can add it to self.view.layer with no problems, but cannot for the life of me get it to appear when added to the UIView.

Here's the simplified code.

- (CAGradientLayer*) makeGradient 
{

    //method returns the gradient layer

    CAGradientLayer *gradeLayer = [CAGradientLayer layer];
    gradeLayer.colors = [NSArray arrayWithObjects:(id)[[UIColor whiteColor] CGColor], (id)[[UIColor blackColor] CGColor], nil];
    gradeLayer.locations =  [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.2], [NSNumber numberWithFloat:0.5], nil];

    return gradeLayer;
}


-(void)addGradient
{

    //method creates UIView, then creates Gradient, and tries to add it to the UIView

    UIView *myView = [[UIView alloc] initWithFrame:self.view.frame];
    myView.backgroundColor = [UIColor clearColor];
    myView.opaque = NO;

    CAGradientLayer *bg = [self makeGradient];
    CGRect myRect = myView.bounds;
    myRect.size.height = myRect.size.height * 5;
    myRect.origin.y = myView.bounds.size.height-myRect.size.height;

    bg.frame = myRect;

    //Adding gradient to self.view.layer works like a charm...
    //[self.view.layer insertSublayer:bg atIndex:0];

    //...however, adding it to my custom view doesn't work at all.
    [myView.layer insertSublayer:bg atIndex:0];

}

What am I missing? Thanks in advance for any insight.

Rich
  • 8,108
  • 5
  • 46
  • 59
wanderingme
  • 745
  • 1
  • 5
  • 7
  • Do you every give `bg` a frame elese where in your code? Would there be an issue using a subclass? – Rich Apr 24 '14 at 20:11
  • @Rich, I don't assign a frame to 'bg' anywhere else in the code. Just this once. What were your thoughts about subclassing? You mean subclassing the UIView? Just trying to be clear. Thanks! – wanderingme Apr 24 '14 at 20:16
  • You can subclass `UIView` and use the `self.layer` directly - would be neater and you don't have to deal with the frames then. I'll post an answer :) – Rich Apr 24 '14 at 20:17
  • Cheers, @Rich! I was going to subclass it once I got it working first. I suppose you're implying [myView.layer insertSublayer:bg atIndex:0] is the culprit here. Thanks for the help! – wanderingme Apr 24 '14 at 20:20
  • Yeah, you should really do `bg.frame = self.layer.bounds;` but if the views `frame` changes you'd have to update `bg` frame again, subclassing is much nicer :) – Rich Apr 24 '14 at 20:24

1 Answers1

0

Your problem is that you don't seem to be setting a frame on the sub layer. Instead a cleaner option is to subclass UIView and use layerClass.

CustomGradientView.h

// For CALayers, make sure you also add the QuartzCore framework
#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>

@interface CustomGradientView : UIView

// Redeclare the layer property but as the new CALayer class so it can receive the correct
// messages without compiler warnings.
@property (nonatomic, strong, readonly) CAGradientLayer *layer;

@end

CustomGradientView.m

#import "CustomGradientView.h"

@interface CustomGradientView ()

@end

@implementation CustomGradientView

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

-(instancetype)init
{
    self = [super init];
    if (self) {
        [self customInit];
    }
    return self;
}

-(instancetype)initWithCoder:(NSCoder *)decoder
{
    self = [super initWithCoder:decoder];
    if (self) {
        [self customInit];
    }
    return self;
}

-(void)customInit
{
    self.layer.colors = [NSArray arrayWithObjects:(id)[[UIColor whiteColor] CGColor], (id)[[UIColor blackColor] CGColor], nil];
    self.layer.locations =  [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.2], [NSNumber numberWithFloat:0.5], nil];
}

@end
Rich
  • 8,108
  • 5
  • 46
  • 59
  • I've moved the `layer` property redefinition to the header file so it can be accessed if needs be. – Rich Apr 24 '14 at 20:26
  • Thanks a bunch! I'm implementing this now. For some reason it's not working in the subclass either. **edit** saw your change to fix compiler errors. Still not working. CustomGradientView *BGView = [[CustomGradientView alloc] initWithFrame:self.view.frame]; – wanderingme Apr 24 '14 at 20:34
  • I did update my answer to define `layer` in the header file, just check again to make sure you've got that in. Oh and also make sure you're importing `QuartzCore` - I assumed you were as you were using `CAGradientLayer` before. – Rich Apr 24 '14 at 20:37
  • I have imported QuartzCore. I've copied and pasted what you have here, and I still cannot get it to work. In my SKScene class, I initialize the class like this: CustomGradientView *BGView = [[CustomGradientView alloc]init]; And still nothing. :( – wanderingme Apr 24 '14 at 20:50
  • Does it not compile, or crash? – Rich Apr 24 '14 at 20:53
  • That's crazy. I'm 100% positive my issue is PEBKAC. I've zipped and uploaded a test project here that shows what I'm doing. Would you mind terribly taking a look? http://1drv.ms/1putVzB – wanderingme Apr 24 '14 at 21:22
  • Ah, you didn't mention you were using `SKView`s! I was about to say, yeah if you don't add the gradient view to anything it won't show! Just gonna have a look and see if I can spot the issue. – Rich Apr 24 '14 at 21:30
  • I have no idea if this is the best way or not but I've changed it to draw an image with gradient in and then set that on a `SKSpriteNode` have a look anyway https://drive.google.com/file/d/0B_o_AQHu6Gg3WklEdFNfYjd1RUU/edit?usp=sharing It shows a gradient now. It updates the gradient when you rotate - the rotations caused some issues, hence the couple of extra methods. EDIT: Also I added `CoreImage` framework, this isn't actually needed and can be taken out. Also meant to say the code I added/changed is only present in `NNViewController` - I did remove `CustomGradientView` from `NNMyScene` too – Rich Apr 24 '14 at 22:16
  • That's actually pretty good. The more I use Sprite Kit, the more it appears to be more trouble that it's worth. Thanks a million for your help, Rich! This is gonna really help. – wanderingme Apr 24 '14 at 22:22
  • No problem! :) Yeah I've never used SpriteKit, does seem a lot of work, but then I don't do games development (I assume that what it is for?!) – Rich Apr 24 '14 at 22:23
  • Yes, it's ultimately for a game. I'm fine with coding the game part once I can get the syntax down. I come from Flash AS3, so Obj-C is a real obstacle. Thanks again! – wanderingme Apr 24 '14 at 22:25