Search code examples
cocoacore-animationcalayer

Clickable CALayer UIs


I have a coverflow-like image carousel grabbing photos w/iPhoto scripting bridge. I have the view set to accept first responder and have implemented methods for left and right arrows, but cannot figure out, conceptually, how to approach making the layers clickable. I know that accepting mouse-down events is the containing view's job, but I can't put the pieces together in my head.

The general point is should I even be trying to make CALayers into UI objects like buttons etc? Because they are very flexible in other ways and I love working with them.

Thanks!


Solution

  • Ok, this is what I pieced together. The context is I have a custom view with one sublayer (CATextLayer) that I'm constraining with the built in layout manager. Here is the awakeFromNib in CustomView.m

    - (void) awakeFromNib {
    
    CALayer *layer = [CALayer layer];
    layer.backgroundColor = [CGColorHolder black];
    layer.layoutManager = [CAConstraintLayoutManager layoutManager];
    [self setLayer:layer];
    [self setWantsLayer:YES];
    
    CATextLayer *textLayer = [CATextLayer layer];
    textLayer.string = @"TextLater";
    textLayer.name = @"textLayer";
    textLayer.fontSize = 42.0f;
    textLayer.alignmentMode = kCAAlignmentCenter;
    [textLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMidX relativeTo:@"superlayer" attribute:kCAConstraintMidX offset:0.0f]];
    [textLayer addConstraint:[CAConstraint constraintWithAttribute:kCAConstraintMaxY relativeTo:@"superlayer" attribute:kCAConstraintMaxY offset:-50.0f]];
    
    }
    

    Then I over-ride the mouseDown method for the custom view. Using -hitTest, I return the layer that contains the point where the mousedown occurred, and then set some attribute of the layer to show that its been hit.

    - (void)mouseDown:(NSEvent *)theEvent {   
    
    CALayer *layer = [self.layer hitTest:NSPointToCGPoint([theEvent locationInWindow])];
    layer.backgroundColor = [CGColorHolder red];
    

    }

    Also, you have to make sure the containing window accepts mouseMoved events:

    [window acceptsMouseMovedEvents];
    

    I don't know how far this will take me in terms of a really sophisticated UI. From here, I will probably delegate what to do to the returned layer based on the name returned. But that could get ugly real fast, so even though I answered my own question here, I would really love to get some input from someone who really knows what they are doing! :D Thanks!