Proposed Challenge Solution


#1

In an earlier question here (Challenge: Who should be delegate?), I had noted that the first challenge hint stated that HypnosisView cannot be the delegate, but that the CALayer documentation states that the delegate property must be set to the UIView object associated with the layer.

Well, I didn’t to it either of those ways. I basically made a new BoxLayer class (subclass of CALayer) and made it be its own delegate. I’m sure there is something not quite right with this, but it seemed to be the cleanest and most encapsulated approach to me (except of course with CGImage drawing the image upside down, which I didn’t fix yet). Critiques, suggestions, etc would be welcome.

Changes to HypnosisView.m:

#import "BoxLayer.h"

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {
        // explicitly retain because convenience method autoreleases
        stripeColor = [[UIColor lightGrayColor] retain];
        
        // add box sublayer with image content
        boxLayer = [[BoxLayer alloc] init];
        UIImage *layerImage = [UIImage imageNamed:@"Hypno@2x.png"];
        [boxLayer setImage:layerImage];
        [[self layer] addSublayer];
        [boxLayer release];
    }
    
    return self;
}

BoxLayer.h

#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>

@interface BoxLayer : CALayer
{
    UIImage *image;
}
@property (nonatomic, retain) UIImage *image;
@end

BoxLayer.m

#import "BoxLayer.h"

@implementation BoxLayer

@synthesize image;

- (id)init
{
    self = [super init];
    if(self) {        
        [self setBounds:CGRectMake(0.0, 0.0, 85.0, 85.0)];
        [self setPosition:CGPointMake(160.0, 100.0)];
        UIColor *reddish = [UIColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.5];
        CGColorRef cgReddish = [reddish CGColor];
        [self setBackgroundColor:cgReddish];
        
        // inset image a bit on each side
        [self setContentsRect:CGRectMake(-0.1, -0.1, 1.2, 1.2)];
        
        // let image resize (w/o changing aspect ratio) to fill contentRect
        [self setContentsGravity:kCAGravityResizeAspect];
        
        // handle drawLayer:inContext: myself
        [self setDelegate:self];
    }
    return self;
}

- (void)setPosition:(CGPoint)position
{
    [super setPosition];
    [self setNeedsDisplay];
}

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
    if(image) {
        float yy = [self position].y;
        CGRect superBounds = [[self superlayer] bounds];
        float alpha = yy / superBounds.size.height;
        CGContextSetAlpha(ctx, alpha);
        CGContextDrawImage(ctx, [self bounds], [image CGImage]);
    }
    [self setNeedsDisplay];
}
@end

#2

Thanks for posting your solution. I’ve just started working on mine and peeked in here for hints.

My understanding of the context of the Challenge was that, given the two ways of drawing w/ CALayer - subclassing or delegating - the Challenge wants us to use the latter. In your solution you’re subclassing CALayer (in addition to providing a delegate) and so seems to go against the objective of the Challenge…?