views:

42

answers:

2

Hi, In relation to question an eralier question of mine, I have tried and failed to create a class with a NSMuttableArray member variable holding CALayerRefs. Can someone please guide me on how to do that. What I want to do is basically create CALayerRefs or CGLayerRefs or whatever, push them into my layers variable, and then, when I need them, fetch, use their context and finally draw/hide/show/delete them.

I turn to you, guys, because apparently, there is few to none information on the net on working with layers and Quartz on an advanced level. Everybody uses the layers right away, no management needed, no member variables.

Thank you.

+1  A: 

CALayerRef does not even exist. If you're talking about CALayer, there shouldn't be a problem. Just add them to the array.

If you mean CGLayerRefs, it is different because those are not Objective-C objects. NSArray only works with objects. If you want to put CGLayerRefs into an array, use a CFMutableArrayRef (the corresponding Core Foundation type) instead.

Ole Begemann
+1  A: 

Here's some working code for custom view I wrote in few minutes, hope it helps. It creates 10 green layers, and animates them each second to different locations.

MBLineLayerDelegate *lineLayerDelegate;
@property (nonatomic, retain) NSMutableArray *ballLayers;

- (void)awakeFromNib
{
    self.ballLayers = [NSMutableArray array];
    lineLayerDelegate = [[MBLineLayerDelegate alloc] init];
    for (NSUInteger i = 0; i < 10; i++) {
        CALayer *ball = [CALayer layer];
        CGFloat x = self.bounds.size.width * (CGFloat)random()/RAND_MAX;
        CGFloat y = self.bounds.size.height * (CGFloat)random()/RAND_MAX;
        ball.frame = CGRectMake(x, y, 20, 20);
        ball.backgroundColor = [UIColor greenColor].CGColor;
        ball.delegate = lineLayerDelegate;
        [self.layer addSublayer:ball];
        [self.ballLayers addObject:ball];
    }

    [self performSelector:@selector(animateBallsToRandomLocation) withObject:nil afterDelay:0];
}

- (void)animateBallsToRandomLocation
{
    for (CALayer *layer in self.ballLayers) {
        CGFloat x = self.bounds.size.width * (CGFloat)random()/RAND_MAX;
        CGFloat y = self.bounds.size.height * (CGFloat)random()/RAND_MAX;
        layer.position = CGPointMake(x, y);
    }
    [self performSelector:@selector(animateBallsToRandomLocation) withObject:nil afterDelay:1];
}

Here's some code for CALayer's delegate that draws a line:

@interface MBLineLayerDelegate : NSObject {
}
- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx;
@end

@implementation MBLineLayerDelegate

- (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context
{
        CGRect rect = layer.bounds;
        CGContextSaveGState(context);

        CGContextTranslateCTM(context, 0.0, rect.size.height);
        CGContextScaleCTM(context, 1.0, -1.0);
        CGContextSetAllowsAntialiasing(context, YES);
        CGContextSetShouldAntialias(context, YES);

        CGContextMoveToPoint(context, 0, 0);
        CGContextAddLineToPoint(context, rect.size.width, rect.size.height);

        CGContextRestoreGState(context);
}

@end
Michal
Say I want to draw a line into one of the layers listed in - (void)animateBallsToRandomLocation . How would I be doing that? Can I get their original context and draw in them?
Interfector
Read my answer for another your question from Today, there I wrote you about how that works - either extending CALayer or setting up a delegate.
Michal
I was hoping it wouldn't get to that :) No I'm going to struggle to implement your answer.
Interfector
I've updated the code above to contain a delegate that draws a line
Michal
Thank you, that's priceless, I don't even want to think how many hours you saved me.
Interfector
After playing with the code last night I realized that while your code accomplishes partly what I need it lacks flexibility. If I understood correctly, you use the delegate to create similar images on different layers. However, I need to draw different stuff on each layer, basically, I want to be able to pass parameters to the drawing function or something similar. Any ideas?
Interfector
Use different delegates for different drawings. Or you can create your own CALayer subclasses, and draw each one differently.
Michal
That would be rater difficult, what I would like to draw are some charts. So I would like each chart line on its own layer, the chart must be created by a function receiving parameters (like an array); I do not have a finite number of drawings, I must have one single function able to draw on one layer respectively. And this is where the problem comes, I do not see a way to pass this data array to the delegate or to the extending class. Am I missing something?
Interfector
Wow, I guess you're missing some very basics of object oriented programming. You can add a property to the delegate, have different instance of delegate for each layer, set delegate's property differently for each layer's delegate, and draw depending on the property's value. What I also do not understand is why do you want to draw each chart into its own CALayer, well, why use CALayer at all, why not drawing into UIView ? Are you going to animate those charts - the only explanation I foresee.
Michal
I am not a beginner in OO programming, but a beginner in iOS development. I will try to implement according to your advice. The thing is I want to show/hide/remove line charts. There is no point in redrawing the whole chart if the user only wants to hide one line chart he is not interested in, is it? Thank you very much for your patience, you have been most helpful
Interfector
You can redraw the whole thing when user requests so, there's no benefit in using layers in such case. Keeping the code simple is more important.
Michal
Well, after some struggling I've made it work, I just didn't see setting parameters of the delegate as a possibility. I just want to say that I added [ball setNeedDisplay]; to show the layer. Thank you again.
Interfector