CALayer - delegate not being called?

  • Hi All!

    I'm playing around with CoreAnim, and having trouble getting delegates
    working. I figure I must be doing something very very wrong here.

    in awakeFromNib, of an NSView that is visible:

    CALayer *main = [self setupLayer];
    self.layer = main;
    self.wantsLayer = YES;

    This shows OK - as in I get a white border around the layer in my view
    - since that's what I've setup in setupLayer.

    - (CALayer*) setupLayer {
    CALayer *layer = [CALayer layer];
    [layer setDelegate:[[[SimpleDelegate alloc] init] retain]];

    // Set the white border so I can tell the layer is there
    [self setupBasicLayerProperties:layer];
    return layer;
    }

    My delegate (SimpleDelegate) does this:
    - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)theContext {
    NSLog(@"go");
    ....
    ...
    ...
    }

    But I never see a "go" in console - it's never called.
    I'm clearly missing something here - anyone got any pointers on
    getting delegates/CALayer working as expected?

    --
    Regards,
    Neil Clayton
  • On Nov 27, 2007, at 2:39 PM, Neil Clayton wrote:

    > I'm clearly missing something here - anyone got any pointers on
    > getting delegates/CALayer working as expected?

    This may be abuse and/or overkill, but when I was working with CA
    recently, I could only get the delegates to be called when I set
    "layer.needsDisplayOnBoundsChange=YES;" (or override +
    (id)defaultValueForKey:(NSString *)key to return [NSNumber
    numberWithBool:YES] for @"needsDisplayOnBoundsChange").

    BTW, this line scares me:

    > [layer setDelegate:[[[SimpleDelegate alloc] init] retain]];

    You have the implicit retain from "init", then you're retaining again,
    then I assume the layer also retains it in setDelegate. Are you sure
    you don't mean "autorelease" instead of "retain"?

    pg
  • On Nov 28, 2007 12:12 AM, Paul Goracke <paul...> wrote:
    > BTW, this line scares me:
    >
    >> [layer setDelegate:[[[SimpleDelegate alloc] init] retain]];
    >
    > You have the implicit retain from "init", then you're retaining again,
    > then I assume the layer also retains it in setDelegate. Are you sure
    > you don't mean "autorelease" instead of "retain"?

    I don't know much about CoreAnimation, but in the delegate model
    delegates are typically *NOT* retained. Autorelease would be
    inappropriate here. The best thing to do would be to store the
    SimpleDelegate instance elsewhere and make sure to release it at the
    end.

    This is only if you're not using garbage collection (in which case the
    release is impotent). But if you're not using GC... then this line
    here could be the whole problem:

    > CALayer *layer = [CALayer layer];

    This needs to be retained somewhere. Otherwise, it gets released after
    the first run through the run loop (which would explain why your
    delegate is never called!).
  • stephen joseph butler wrote:

    > On Nov 28, 2007 12:12 AM, Paul Goracke <paul...> wrote:
    >> BTW, this line scares me:
    >>
    >>> [layer setDelegate:[[[SimpleDelegate alloc] init] retain]];
    >>
    >> You have the implicit retain from "init", then you're retaining
    >> again,
    >> then I assume the layer also retains it in setDelegate. Are you sure
    >> you don't mean "autorelease" instead of "retain"?
    >
    > I don't know much about CoreAnimation, but in the delegate model
    > delegates are typically *NOT* retained. Autorelease would be

    > inappropriate here. The best thing to do would be to store the
    > SimpleDelegate instance elsewhere and make sure to release it at the
    > end.

    I'll go along with that; I didn't find specifics when I did a _very_
    cursory search. Since I tend to use 'self' as a delegate, I rarely run
    into the situation of whether to retain a delegate created solely for
    that purpose. Maybe autorelease isn't appropriate, but is retain-
    following-init ever necessary? As there seems to be no held reference
    to the SimpleDelegate here, where would he call release since the
    layer won't do it?

    >> CALayer *layer = [CALayer layer];
    >
    > This needs to be retained somewhere. Otherwise, it gets released after
    > the first run through the run loop (which would explain why your
    > delegate is never called!).

    I believe "self.layer = main" is retaining it before it can get
    autoreleased.

    pg
  • On Nov 28, 2007 1:02 AM, Paul Goracke <paul...> wrote:
    > I'll go along with that; I didn't find specifics when I did a _very_
    > cursory search. Since I tend to use 'self' as a delegate, I rarely run
    > into the situation of whether to retain a delegate created solely for
    > that purpose. Maybe autorelease isn't appropriate, but is retain-
    > following-init ever necessary?

    Nope, you're absolutely right. I can't think of a reason to alloc-init-retain.

    > As there seems to be no held reference
    > to the SimpleDelegate here, where would he call release since the
    > layer won't do it?

    He could call [[self.layer delegate] release] before releasing the
    layer itself. Personally, I'd stuff it in a property or ivar.

    > I believe "self.layer = main" is retaining it before it can get
    > autoreleased.

    Good question. I haven't been able to take a look at the Obj-C 2.0
    stuff yet (my projects have all been PHP lately... grr) but taking a
    quick look at the docs ISTM that properties are only assigned by
    default, not retained. This *should* generate a compiler warning
    though.

    So back to you, Neil Clayton. Couple questions:

    1) Are you using GC?
    2a) Are you using "@property(retain) CALayer *layer" or just
    "@property CALayer *layer"?
    2b) And if it's the latter, are you getting a compiler warning that
    you're ignoring?
  • >
    >> CALayer *layer = [CALayer layer];
    >
    > This needs to be retained somewhere. Otherwise, it gets released after
    > the first run through the run loop (which would explain why your
    > delegate is never called!).

    when you add the layer to the view it'll get retained.

    The problem in this case is that layers do not automatically 'draw'
    the content using the delegate methods. You must explicitly call the
    setNeedsDisplay method (or set that it needs display on bounds change,
    but in this case that isn't necessary)

    calling setNeedsDisplay causes the delegate method to be called and
    the data to be cached.  that cached data is then used as the 'content'
    for the layer.
  • Hi Scott,

    But Neil is saying that the border of the layer is visible, thus the
    layer must be drawing itself at some point already, right?

    --
    John Clayton

    On Nov 28, 2007, at 9:16 AM, Scott Anguish wrote:

    >>
    >>> CALayer *layer = [CALayer layer];
    >>
    >> This needs to be retained somewhere. Otherwise, it gets released
    >> after
    >> the first run through the run loop (which would explain why your
    >> delegate is never called!).
    >
    > when you add the layer to the view it'll get retained.
    >
    > The problem in this case is that layers do not automatically 'draw'
    > the content using the delegate methods. You must explicitly call the
    > setNeedsDisplay method (or set that it needs display on bounds
    > change, but in this case that isn't necessary)
    >
    > calling setNeedsDisplay causes the delegate method to be called and
    > the data to be cached.  that cached data is then used as the
    > 'content' for the layer.
  • Hi Neil,

    you have to tell the layer it needs a display, it does not know that
    by its self.

    HTH,

    -bd-
    http://bill.dudney.net/roller/objc

    On Nov 27, 2007, at 3:39 PM, Neil Clayton wrote:

    > Hi All!
    >
    > I'm playing around with CoreAnim, and having trouble getting
    > delegates working. I figure I must be doing something very very
    > wrong here.
    >
    > in awakeFromNib, of an NSView that is visible:
    >
    > CALayer *main = [self setupLayer];
    > self.layer = main;
    > self.wantsLayer = YES;
    >
    > This shows OK - as in I get a white border around the layer in my
    > view - since that's what I've setup in setupLayer.
    >
    >
    > - (CALayer*) setupLayer {
    > CALayer *layer = [CALayer layer];
    > [layer setDelegate:[[[SimpleDelegate alloc] init] retain]];
    >
    > // Set the white border so I can tell the layer is there
    > [self setupBasicLayerProperties:layer];
    > return layer;
    > }
    >
    > My delegate (SimpleDelegate) does this:
    > - (void)drawLayer:(CALayer *)layer inContext:
    > (CGContextRef)theContext {
    > NSLog(@"go");
    > ....
    > ...
    > ...
    > }
    >
    > But I never see a "go" in console - it's never called.
    > I'm clearly missing something here - anyone got any pointers on
    > getting delegates/CALayer working as expected?
    >
    > --
    > Regards,
    > Neil Clayton
  • On Nov 28, 2007, at 2:21 AM, John Clayton wrote:

    > But Neil is saying that the border of the layer is visible, thus the
    > layer must be drawing itself at some point already, right?

    The CALayer style properties (described at <http://developer.apple.com/documentation/Cocoa/Conceptual/CoreAnimation_gui
    de/Articles/LayerVisProps.html
    >) are applied "regardless of type of media a layer displays."

    Providing/drawing the CALayer's _content_ requires either setting the
    content property, providing a delegate, or subclassing:
    <http://developer.apple.com/documentation/Cocoa/Conceptual/CoreAnimation_gui
    de/Articles/ProvidingCALayerContent.html
    >

    pg
  • Well, it isn't quite the same.. not drawing, but compositing.

    The border is a style attribute (and is an animatable property).  All
    animatable properties are applied by the render-tree when the layer is
    displayed in the UI.  So they get composited when you change the
    values via the implied animation (unless of course that has been
    disabled for the transaction in which case they still will update and
    draw, but without interpolating the values from the current value to
    the new value).

    You can imagine that if the content was redrawn (by the delegate)
    every time a value changed and animated (so potentially hundreds of
    times for a single animation) that you'd lose most of the advantages
    of Core Animation.

    Regardless, animatable properties are not aren't cached and are
    separate from the content caching process.

    You don't have to, and should not, call setNeedsDisplay for any layers
    that do not have a delegate that provides the content. This will
    create a content cache and cause more memory to be used for the cached
    content.  For larger empty layers that are used only to contain other
    layers it is unnecessary.

    I'll spend some time clarifying this in the Core Animation Programming
    Guide in both the Providing Layer Content and Layer Style Properties
    chapters.

    On Nov 28, 2007, at 5:21 AM, John Clayton wrote:

    > Hi Scott,
    >
    > But Neil is saying that the border of the layer is visible, thus the
    > layer must be drawing itself at some point already, right?
  • THAT is a nice explanation Scott.  That particular separation between
    layer compositing attributes and the actual 'contents' itself wasn't
    something that was really at the forefront of my thought there...
    thanks for that.

    Now it makes more sense... the delegates are *only* used when getting
    the contents property - and thats it.  Specifically, they are not
    called when any animation properties are being interpolated or
    compositing is being performed.

    Enlightenment... the 'bing' feeling... aaaahhhhhhhhh euphoria.  Ta

    --
    john

    On 28/11/2007, at 7:43 PM, Scott Anguish wrote:

    > Well, it isn't quite the same.. not drawing, but compositing.
    >
    > The border is a style attribute (and is an animatable property).
    > All animatable properties are applied by the render-tree when the
    > layer is displayed in the UI.  So they get composited when you
    > change the values via the implied animation (unless of course that
    > has been disabled for the transaction in which case they still will
    > update and draw, but without interpolating the values from the
    > current value to the new value).
    >
    > You can imagine that if the content was redrawn (by the delegate)
    > every time a value changed and animated (so potentially hundreds of
    > times for a single animation) that you'd lose most of the advantages
    > of Core Animation.
    >
    > Regardless, animatable properties are not aren't cached and are
    > separate from the content caching process.
    >
    > You don't have to, and should not, call setNeedsDisplay for any
    > layers that do not have a delegate that provides the content. This
    > will create a content cache and cause more memory to be used for the
    > cached content.  For larger empty layers that are used only to
    > contain other layers it is unnecessary.
    >
    > I'll spend some time clarifying this in the Core Animation
    > Programming Guide in both the Providing Layer Content and Layer
    > Style Properties chapters.
    >
    >
    > On Nov 28, 2007, at 5:21 AM, John Clayton wrote:
    >
    >> Hi Scott,
    >>
    >> But Neil is saying that the border of the layer is visible, thus
    >> the layer must be drawing itself at some point already, right?
    >
  • Thank you all for the msgs - a simple call to setNeedsDisplay /
    needsDisplayOnBoundsChange = YES is doing the trick.  FYI  - the
    retain was simply a carry over from desperation and should have been
    removed from the spike code.

    --
    Regards,
    Neil Clayton,  http://shinywhitebox.com

    On 28/11/2007, at 9:16 PM, Scott Anguish wrote:

    >>
    >>> CALayer *layer = [CALayer layer];
    >>
    >> This needs to be retained somewhere. Otherwise, it gets released
    >> after
    >> the first run through the run loop (which would explain why your
    >> delegate is never called!).
    >
    > when you add the layer to the view it'll get retained.
    >
    > The problem in this case is that layers do not automatically 'draw'
    > the content using the delegate methods. You must explicitly call the
    > setNeedsDisplay method (or set that it needs display on bounds
    > change, but in this case that isn't necessary)
    >
    > calling setNeedsDisplay causes the delegate method to be called and
    > the data to be cached.  that cached data is then used as the
    > 'content' for the layer.
    >
    >
    >
previous month november 2007 next month
MTWTFSS
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    
Go to today