Drawing little clock icons dynamically.

It’s kind of nice to be able to dynamically draw little ‘clock’ time icons. This function returns a UIImage of 30×30 pixels containing such an image:

static inline float radians(double degrees) { return (degrees * M_PI) / 180; }

- (UIImage *)drawTime:(NSNumber *)time {
	UIImage *image;
	CGRect rect = CGRectMake(0,0,30,30);
	UIGraphicsBeginImageContext(rect.size);
	CGContextRef context = UIGraphicsGetCurrentContext();
	// Calculate end angle in radians according to time..... e.g. 60mins == 360o == 360*M_PI/180
	CGFloat endAngle = radians((360/60)*[time doubleValue]);
		
	// Set colors
	CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
	CGContextSetFillColorWithColor(context, [[UIColor grayColor] CGColor]);
	// Set Pen width.
	CGContextSetLineWidth(context, 1);

	// Draw outer circle
	CGContextStrokeEllipseInRect(context, CGRectMake(1, 1, 28, 28));
	
	// Set rect to top left of where we want next circle
	CGContextStrokeEllipseInRect(context, CGRectMake(4, 4, 22, 22));
	
	// Draw segment as grey
	CGContextMoveToPoint(context, 15.0f, 15.0f);
	CGContextBeginPath(context);
	// Draw radius out so we end up with a segment drawn and NOT a chord
	CGContextAddLineToPoint(context, 15, 5);
	// Draw arc, note documentation states final argument is 1 for Clockwise and 0 for CCW.
	// However, iOS inverts the Y axis so 0 for CCW becomes CW.  OF COURSE!
	CGContextAddArc(context, 15, 15, 10, 0, endAngle, 0);
	// Add line back to middle
	CGContextAddLineToPoint(context, 15, 15);
	CGContextClosePath(context);
	CGContextFillPath(context);
		
	// Gimme ma image.
	image = UIGraphicsGetImageFromCurrentImageContext();

	/* Due more core graphics weirdness note from the documentation that:
	 startAngle
	 The angle to the starting point of the arc, measured in radians from the positive x-axis.
	 
	   Thus the resulting image will need a -90o transformation to look like a clock.  E.g. 0 is North
	 
	   Simple way is to apply an affine rotation transformation to the view as it will rotate from the centre point.
	   The CG Rotate will perform from the origin so you would need to translate, rotate, translate seeing as 
	   The CG instance has it's Y axis inversed.  What crack smoking @!$% came up with this co-ordinate
	   system?  This isn't 3D graphics people.  It's 2D  stick to one co ordinate system not 2!
	 */
	// Close context
	UIGraphicsEndImageContext();
	
	return image;
}

Input value must be between 0 and 60 minutes. Also the result image will need a 90degree CCW transform applied, due to co-ordinate changes between Core Graphics and main iOS you may find, depending on where one applies it, that it becomes a 90 degree CW rotation. Ho hum. For values over 60 minutes I would recommend a helper function to draw several clocks alongside one another and return that resulting image.

UPDATE:
This entry continues this tale…

This entry was posted in Programming and tagged , , , , , , . Bookmark the permalink.

One Response to Drawing little clock icons dynamically.

  1. Diziet says:

    As an addendum. I punt these images straight into a UIImageView so here is the code and in active use with the transform:

    	[self.imgPreparationTime setImage:[self drawTime:recipe.preparationtime]];
    	// Apply -90 transform.
    	self.imgPreparationTime.transform = CGAffineTransformMakeRotation(-M_PI/2);
    

Leave a Reply