Bronze + Gold challenge solution


#1
- (void)drawRect:(CGRect)rect
{
    
    
    
    // Circle bounds
    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;
    UIBezierPath *path = [[UIBezierPath alloc] init];
    for (float currentRadius = maxRadius; currentRadius > 0; currentRadius -= 20) {
        
        [path moveToPoint:CGPointMake(center.x + currentRadius, center.y)];
        [path addArcWithCenter:center
                        radius:currentRadius
                    startAngle:0.0
                      endAngle:M_PI * 2.0
                     clockwise:YES];
        
    }
    
    path.lineWidth = 10;
    [[UIColor lightGrayColor] setStroke];
    [path stroke];
    
    
    
    // -------- BRONZE & GOLD --------
    
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    //important to place the save in front of the part wich contains clipping path
    CGContextSaveGState(currentContext);
   

    // Drawing triangle
    UIBezierPath *trianglePath = [[UIBezierPath alloc] init];
    CGPoint triangleOne = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect) + 100);
    CGPoint triangleTwo = CGPointMake(CGRectGetMaxX(rect) - 50, CGRectGetMaxY(rect) - 100);
    CGPoint triangleThree = CGPointMake(CGRectGetMinX(rect) + 50, CGRectGetMaxY(rect) - 100);
    [trianglePath moveToPoint:triangleOne];
    [trianglePath addLineToPoint:triangleTwo];
    [trianglePath addLineToPoint:triangleThree];
     //adding clipping mask
    [trianglePath addClip];
    
    
    // gradient
    CGFloat locations[2] = { 0.0, 1.0 };
    CGFloat components[8] = { 2.0, 0.0, 4.0, 5.0,
                              1.0, 1.0, 0.0, 1.0};
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorspace, components, locations, 2);
    CGPoint startpoint = CGPointMake(0, 0);
    CGPoint endpoint = CGPointMake(bounds.size.width, bounds.size.height);
    CGContextDrawLinearGradient(currentContext, gradient, startpoint, endpoint, 0);
    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorspace);
    
    
    //important to place the restore in front of the 'logo code'
    // otherwise it will be clipped as well
    CGContextRestoreGState(currentContext);
    
    
    //logo
    UIImage *logo = [UIImage imageNamed:@"logo.png"];
    // putting the logo right in the middle
    float rightX = bounds.size.width / 2 - (logo.size.width / 4);
    float righty = bounds.size.height / 2 - (logo.size.height / 4);
    CGRect logoRect = CGRectMake(rightX, righty, logo.size.width / 2, logo.size.height / 2);
    CGContextSetShadow(currentContext, CGSizeMake(4,10), 3);
    [logo drawInRect];
    
    
    
    
}

#2

Pretty much the same except created CGPoint inline

// Drawing code CGRect bounds = self.bounds; // Figure out the center of the bounds rectangle CGPoint center; center.x = bounds.origin.x + bounds.size.width / 2.0; center.y = bounds.origin.y + bounds.size.height / 2.0; UIBezierPath *mypath = [[UIBezierPath alloc] init]; [mypath moveToPoint:CGPointMake(center.x, center.y-210)]; [mypath addLineToPoint:CGPointMake(center.x-150, center.y+70)]; [mypath addLineToPoint:CGPointMake(center.x+150, center.y+70)]; [mypath addClip];


#3

Maybe not as elegant as the other solutions using UIBezierPath, but now I know what’s under the hood.

- (void)drawRect:(CGRect)rect
{
    // Drawing code
	CGRect bounds = self.bounds;
	
	// Figure out the center of the bounds rectangle
	CGPoint center;
	center.x = bounds.origin.x + bounds.size.width / 2.0;
	center.y = bounds.origin.y + bounds.size.height / 2.0;
	
	// The largest circle will circumscribe the view
	float maxRadius = hypot(bounds.size.width, bounds.size.height) / 2.0;
	
	UIBezierPath *path = [[UIBezierPath alloc]init];
	
	for (float currentRadius = maxRadius; currentRadius > 0; currentRadius -=20) {
		[path moveToPoint:CGPointMake(center.x + currentRadius, center.y)];
		[path addArcWithCenter:center
						radius:currentRadius
					startAngle:0.0
					  endAngle:M_PI * 2 clockwise:YES];
	}
	
	// Configure line width to 10 points
	path.lineWidth = 10;
	
	// Configure the drawing color to light gray
	UIColor *color = [UIColor lightGrayColor];
	[color setStroke];
	
	// Draw line
	[path stroke];
	
	/*** BRONZE AND GOLD CHALLENGE ***/
	
	/*** MAKE TRIANGLE WITH GRADIENT ***/
	
	// Get graphics context and save the current state
	CGContextRef currentContext = UIGraphicsGetCurrentContext();
	CGContextSaveGState(currentContext);
		
	// THE TRIANGLE

	// Make Array with CGPoints of a triangle relative to center of frame  bounds (CGPoint center)
	CGPoint trianglePoints [] = {
		CGPointMake(center.x, center.y - 190), // starting point
		CGPointMake(center.x + 130, center.y + 190), // lower right point
		CGPointMake(center.x - 130, center.y + 190), // lower left point
		CGPointMake(center.x, center.y -190) // ending point (same as starting p.)
	};
	
	// Draw path and clip context
	CGContextAddLines(currentContext, trianglePoints, 4);
	CGContextClip(currentContext);

	// THE GRADIENT
	
	// Declare myGradient and myColorSpace and set number of locations
	CGGradientRef myGradient;
	CGColorSpaceRef myColorSpace;
	size_t num_locations = 2;
	
	// Make array of locations and set start and end values
	CGFloat locations[2] = {0.0, 1.0};
	
	// Set colors
	CGFloat components[8] = {
		1.0,0.0,0.0,1.0,  // Start color is red
		1.0, 1.0, 0.0, 1.0   // End color yellow
	};
	
	// Set RGB color space for iOS devices
	myColorSpace = CGColorSpaceCreateDeviceRGB();
	
	// Create Gradient
	myGradient = CGGradientCreateWithColorComponents (myColorSpace, components, locations, num_locations);
	
	// Declare and set gradient start and endpoint
	CGPoint myStartPoint, myEndPoint;
	myStartPoint.x = center.x;
	myStartPoint.y = center.y - 190;
	myEndPoint.x = center.x;
	myEndPoint.y = center.y + 190;
	
	// Draw gradient
	CGContextDrawLinearGradient(currentContext, myGradient, myStartPoint, myEndPoint, 0);
	
	CGGradientRelease(myGradient);
	CGColorSpaceRelease(myColorSpace);
	
	/*** IMAGE WITH SHADOW ***/
	
	// Restore context to the most recently saved (without clip)
	CGContextRestoreGState(currentContext);
	
	// Set offsetSize and shadow
	CGSize offsetSize = CGSizeMake(4, 7);
	CGContextSetShadow(currentContext, offsetSize, 2);
	
	// Load image to rect
	UIImage *logoImage = [UIImage imageNamed:@"logo.png"];
	
	// Image rect position and re-size image
	CGFloat x = center.x - ((logoImage.size.width * 0.6) / 2);
	CGFloat y = center.y - ((logoImage.size.height * 0.6) / 2);
	
	CGFloat w = logoImage.size.width * 0.6;
	CGFloat h = logoImage.size.height * 0.6;
	
	// Place image into rect
	[logoImage drawInRect:CGRectMake(x, y, w, h)];
	
}

#4

All looks good on these examples. My code is also very similar with a few minor changes, but the book does give us most of the gradient code anyway lol. One thing I noticed in most of the examples posted here is that instead of starting from the tip of the triangle and filling the gradient to the bottom of the triangle you are starting in the origin point (top left corner) and filling through the entire screen. This takes a little away from the gradient. You can set the start point to be the tip of the circle (point is already created btw) and the end point to be the midpoint of the bottom of the circle to get the same type of fill as the example image at the end of the challenge.

That being said, I kinda like having the gradient run in more of a diagonal route rather than top to bottom :slight_smile: its just that you don’t see the effect as well.

[code]//
// JMRHypnosisView.m
// Hypnosister
//
// Created by Matthew Raby on 10/7/14.
// Copyright © 2014 Matthew Raby. All rights reserved.
//

#import “JMRHypnosisView.h”

@implementation JMRHypnosisView

  • (id)initWithFrame:(CGRect)frame
    {
    self = [super initWithFrame:frame];
    if (self) {
    // all jmrhypnosisviews start with a clear background color
    self.backgroundColor = [UIColor clearColor];
    }
    return self;
    }

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.

  • (void)drawRect:(CGRect)rect
    {
    CGRect bounds = self.bounds;
    // figure out the center of the bounds rectangle
    CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
    //center.x = bounds.origin.x + bounds.size.width / 2.0;
    //center.y = bounds.origin.y + bounds.size.height / 2.0;

    // the largest circle will circumscribe the view
    float maxRadius = hypot(bounds.size.width, bounds.size.height);

    UIBezierPath *path = [[UIBezierPath alloc]init];

    for (float currentRadius = maxRadius; currentRadius >0; currentRadius -= 20){
    [path moveToPoint:CGPointMake(center.x + currentRadius, center.y)];
    [path addArcWithCenter:center
    radius:currentRadius
    startAngle:0.0
    endAngle:M_PI*2
    clockwise:YES];
    }
    path.lineWidth = 10;
    [[UIColor lightGrayColor]setStroke];
    [path stroke];

    /********** DRAW TRIANGLE ON SCREEN **********/

    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextSaveGState(currentContext);

    // draw the triangle
    UIBezierPath *triangle = [[UIBezierPath alloc]init];
    // create three points for the triangle
    CGPoint top = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)-150);
    CGPoint left = CGPointMake(CGRectGetMidX(rect)-95, CGRectGetMidY(rect)+160);
    CGPoint right = CGPointMake(CGRectGetMidX(rect)+95, CGRectGetMidY(rect)+160);
    [triangle moveToPoint:top];
    [triangle addLineToPoint:left];
    [triangle addLineToPoint:right];
    //[triangle addLineToPoint:top]; Makes no difference to the final objective and filling the triangle with the gradient.
    //returning to the top point isn’t needed
    [triangle addClip];

    // Create the gradient
    CGFloat locations[2] = {0.0, 1.0};
    CGFloat components[8] = {0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0};
    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorspace, components, locations, 2);
    CGPoint startpoint = top;
    CGPoint endpoint = CGPointMake(CGRectGetMidX(rect),CGRectGetMidY(rect)+160);
    CGContextDrawLinearGradient(currentContext, gradient, startpoint, endpoint, 0);
    CGGradientRelease(gradient);
    CGColorSpaceRelease(colorspace);
    // Restore state
    CGContextRestoreGState(currentContext);

    // add logo and give it a shadow
    CGContextSetShadow(currentContext, CGSizeMake(4, 7), 3);
    UIImage *logoImage = [UIImage imageNamed:@“logo.png”];
    [logoImage drawInRect:CGRectMake(bounds.size.width/2 - logoImage.size.width/4,
    bounds.size.height/2 - logoImage.size.height/4,
    logoImage.size.width/2,
    logoImage.size.height/2)];

}
[/code]