Bronze & Silver & Gold - Completed


#1

[color=#444]Hello, everyone! Here’s how I approached these challenges.

[size=150] ☆ Bronze Challenge[/size]
Make the circles appear in assorted colors.

[size=150] ☆ Silver Challenge[/size]
Make every HypnosisView instance have green crosshair in the middle. The crosshair should not have a shadow, but it should be drawn on top of the text.

[size=150] ☆ Gold Challenge[/size]
Make another UIView subclass that draws the BNR logo. The logo should be clipped to a circle, have a black outline with a shadow underneath and have a slightly blue gradient coming from the top of the circle to the center. Make this view instance a subview of HypnosisView.[/color]

[color=#600000][size=200]HypnosisterAppDelegate.h[/size][/color]

@class HypnosisView; @class BNRLogoView; @interface HypnosisterAppDelegate : UIResponder <UIApplicationDelegate, UIScrollViewDelegate> { HypnosisView *view; BNRLogoView *icon; } @property (strong, nonatomic) UIWindow *window; @end
[color=#000060][size=200]HypnosisterAppDelegate.m[/size][/color]
[color=#008040]- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
[/color]

[code]CGRect screenRect = [[self window] bounds];

view = [[HypnosisView alloc] initWithFrame:screenRect];
[scrollView addSubview:view];

icon = [[BNRLogoView alloc] initWithFrame:screenRect];
[view addSubview:icon];
[/code]
[color=#000060][size=200]HypnosisView.m[/size][/color]
[color=#008040]- (id)initWithFrame:(CGRect)frame;[/color]

self = [super initWithFrame:frame]; if (self) { [self setOpaque:YES]; // improves performance #1 [self setBackgroundColor:[UIColor whiteColor]]; } return self;
[ul][size=85]1. If background isn’t transparent, setting view “opaque” prevents view from doing unnecessary compositing calculations.[/size][/ul]
[color=#008040]-(void)drawCrosshairsInContext:(CGContextRef)ctx;[/color]

[code]CGContextSaveGState(ctx);
//==========================================================================
// Crosshair Stroke Parameters
CGContextSetShadowWithColor(ctx, CGSizeMake(0, 0), 0.0, NULL);
CGContextSetRGBStrokeColor(ctx, 0.0, 1.0, 0.0, 0.65);
CGRect bounds = [self bounds];
CGPoint center;
center.x = bounds.origin.x + bounds.size.width / 2.0;
center.y = bounds.origin.y + bounds.size.height / 2.0;

// Calculate screen height/width difference #1
CGFloat diff = fabsf(bounds.size.height - bounds.size.width) / 2.0;

// Create Path and stroke
UIBezierPath *path = [UIBezierPath bezierPath];
[path setLineWidth:10.0];
[path moveToPoint:CGPointMake(center.x, center.y - 10)];
[path addLineToPoint:CGPointMake(center.x, center.y - 55)];
[path stroke];

// Rotate path and stroke
for (int i = 0; i < 3; i++) {
CGContextRotateCTM(ctx, M_PI_2); // #2
CGContextTranslateCTM(ctx, diff, (bounds.size.height - diff) * -1.0); // #3
[path stroke];
}
//==========================================================================
CGContextRestoreGState(ctx);[/code]
[ul][size=85]CTM = Current Transform Matrix

  1. The CGContextRef is rotated on its origin. Because screen rect is rectangular (not square), we need to calculate the height/width difference and divide the remainder by 2 (accounting for top/bottom margin) to determine the difference. fabsf() returns an absolute float.
  2. Rotate the CGContextRef by 90 degrees (angle is specified in radians). Rotating the CTM alters the coordinate system of the page (x/y axis relative to super view).
  3. Reposition the CGContextRef. [/size][/ul]
    [color=#008040]- (void)drawRect:(CGRect)rect;[/color]

[code]CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect bounds = [self bounds];

CGPoint center;
center.x = bounds.origin.x + bounds.size.width / 2.0;
center.y = bounds.origin.y + bounds.size.height / 2.0;

float maxRadius = hypot(bounds.size.width, bounds.size.height) / 2.0;
CGContextSetLineWidth(ctx, 10);

float hue = 0.0;
for (float currentRadius = maxRadius; currentRadius > 0; currentRadius -= 20) {
[[UIColor colorWithHue:hue
saturation:0.6
brightness:0.7
alpha:1.0] setStroke];

CGContextAddArc(ctx, center.x, center.y, currentRadius, 0.0, M_PI * 2.0, YES);
CGContextStrokePath(ctx);
    
hue += 0.1;

}

NSString *text = @“You are getting sleepy”;
UIFont *font = [UIFont boldSystemFontOfSize:28];

CGRect textRect;
textRect.size = [text sizeWithFont:font];
textRect.origin.x = center.x - textRect.size.width / 2.0;
textRect.origin.y = center.y - textRect.size.height / 2.0;

[[UIColor blackColor] setFill];

CGSize offset = CGSizeMake(4, 3);
CGColorRef color = [[UIColor darkGrayColor] CGColor];
CGContextSetShadowWithColor(ctx, offset, 2.0, color);
[text drawInRect:textRect withFont:font];

[self drawCrosshairsInContext:ctx];[/code]
[color=#600000][size=200]BNRLogoView.h[/size][/color]

#import <Foundation/Foundation.h> @interface BNRLogoView : UIView @property (nonatomic, readonly, strong) UIImage *icon; @end
[color=#000060][size=200]BNRLogoView.m[/size][/color]
[color=#008040]- (id)initWithFrame:(CGRect)frame;[/color]

self = [super initWithFrame:frame]; if (self) { [self setBackgroundColor:[UIColor clearColor]]; _icon = [UIImage imageNamed:@"Icon"]; } return self;
[color=#008040]- (UIBezierPath *)drawCGBezierCirlce:(CGContextRef)ctx
inFrame:(CGRect)frame
withRadius:(CGFloat)radius;
[/color]

[code]UIBezierPath *circle = [UIBezierPath bezierPath];

CGPoint center;
center.x = frame.origin.x + radius;
center.y = frame.origin.y + radius;

[circle addArcWithCenter:center
radius:radius
startAngle:0.0f
endAngle:(M_PI * 2.0f)
clockwise:YES];

return circle;[/code]
[color=#008040]- (void)drawRect:(CGRect)rect;[/color]

[code]CGContextRef ctx = UIGraphicsGetCurrentContext();
rect = CGRectInset(rect, 3.0f, 3.0f); // create _icon boundary

//--------------------------------------------------------------------------
// CIRCLE PATH
//--------------------------------------------------------------------------
CGSize iconSize = [_icon size];
CGFloat scale = MIN((rect.size.width / iconSize.width),
(rect.size.height / iconSize.height)) / 3.25f;

iconSize.width *= scale;
iconSize.height *= scale;

UIBezierPath *matte = [self drawCGBezierCirlce:ctx
inFrame:rect
withRadius:iconSize.width/2.0f];

//--------------------------------------------------------------------------
// SHADOW
//--------------------------------------------------------------------------
CGContextSaveGState(ctx);

CGContextSetShadowWithColor(ctx,
CGSizeMake(2.0f, 3.0f),
3.0f,
[[UIColor darkGrayColor] CGColor]);
[[UIColor blackColor] setFill];
[matte fill];

CGContextRestoreGState(ctx);

//--------------------------------------------------------------------------
// CLIP + GRADIENT
//--------------------------------------------------------------------------
CGContextSaveGState(ctx);
//==========================================================================
[matte addClip];

CGRect iconRect = CGRectMake( rect.origin.x,
rect.origin.y,
iconSize.width,
iconSize.height);
[_icon drawInRect];

CGColorSpaceRef colorSpace;
CGGradientRef gradient;

size_t numLocation = 2;
CGFloat location[2] = {0.0f, 0.75f};
CGFloat components[8] = {0.0f, 0.0f, 1.0f, 0.75f, // Start color
0.0f, 0.0f, 1.0f, 0.0f}; // End Color

colorSpace = CGColorSpaceCreateDeviceRGB();
gradient = CGGradientCreateWithColorComponents(colorSpace,
components,
location,
numLocation);

CGPoint startPoint, endPoint;
startPoint.x = iconRect.origin.y;
startPoint.y = 0.0f;
endPoint.x = 1.0f;
endPoint.y = iconRect.origin.y + iconRect.size.height;

CGContextDrawLinearGradient(ctx,
gradient,
startPoint,
endPoint,
0);

CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
//==========================================================================
CGContextRestoreGState(ctx);

//--------------------------------------------------------------------------
// STROKE
//--------------------------------------------------------------------------
CGContextSaveGState(ctx);
CGContextSetAllowsAntialiasing(ctx, YES);
CGContextSetShouldAntialias(ctx, YES);
[[UIColor blackColor] setStroke];
[matte setLineWidth:2.0f];
[matte stroke];
CGContextRestoreGState(ctx);[/code]