objective c - Implementing a clock face in UIView -
i'm trying implement clock face in uiview - without luck. problem hands on clock face end in totally wrong position (i.e. pointing wrong time). i'm pretty sure logic of code correct - there's wrong.
my test app ios single view application. viewcontroller.h looks this:
#import <uikit/uikit.h> @interface clock : uiview { uicolor* strokecolour; uicolor* fillcolour; float strokewidth; nsdate* clocktime; cgpoint clockcentre; float clockradius; } - (id)initwithcentre:(cgpoint)centre radius:(float)radius borderwidth:(float)width facecolour:(uicolor*)fill handcolour:(uicolor*)handfill time:(nsdate*)time; @end @interface viewcontroller : uiviewcontroller @end
and viewcontroller.m looks this:
#import "viewcontroller.h" @implementation clock - (id)initwithcentre:(cgpoint)centre radius:(float)radius borderwidth:(float)width facecolour:(uicolor*)fill handcolour:(uicolor*)handfill time:(nsdate*)time { cgrect frame = cgrectmake(centre.x-radius, centre.y-radius, radius*2, radius*2); if (self = [super initwithframe:frame]) { // initialization code strokecolour = fill; fillcolour = handfill; strokewidth = width; clockradius = radius; clocktime = time; clockcentre.x = frame.size.width/2; clockcentre.y = frame.size.height/2; } return self; } - (void)drawrect:(cgrect)rect { double red, green, blue, alpha; cgrect circlerect; circlerect.origin.x = rect.origin.x+strokewidth; circlerect.origin.y = rect.origin.y+strokewidth; circlerect.size.width = rect.size.width - (strokewidth*2); circlerect.size.height = rect.size.height - (strokewidth*2); cgcontextref contextref = uigraphicsgetcurrentcontext(); cgcontextsetlinewidth(contextref, strokewidth); [fillcolour getred:&red green:&green blue:&blue alpha:&alpha]; cgcontextsetrgbfillcolor(contextref, red, green, blue, alpha); [strokecolour getred:&red green:&green blue:&blue alpha:&alpha]; cgcontextsetrgbstrokecolor(contextref, red, green, blue, alpha); cgcontextfillellipseinrect(contextref, circlerect); cgcontextstrokeellipseinrect(contextref, circlerect); cgcontextsetlinewidth(contextref, strokewidth); [strokecolour getred:&red green:&green blue:&blue alpha:&alpha]; cgcontextsetrgbstrokecolor(contextref, red, green, blue, alpha); float hourangle, minuteangle, secondangle, angle; double endx, endy; nscalendar *cal = [[nscalendar alloc]initwithcalendaridentifier:nscalendaridentifiergregorian]; nsuinteger units = nscalendarunithour | nscalendarunitminute | nscalendarunitsecond; nsdatecomponents *components = [cal components:units fromdate:clocktime]; hourangle = (components.hour /12.0) * m_pi * 2.0; minuteangle = (components.minute / 60.0) * m_pi * 2.0; secondangle = (components.second / 60.0) * m_pi * 2.0; //minute hand angle = minuteangle; endx = cos(angle) * (clockradius*0.85) + clockcentre.x; endy = sin(angle) * (clockradius*0.85) + clockcentre.y; cgcontextmovetopoint(contextref,clockcentre.x,clockcentre.y); cgcontextaddlinetopoint(contextref,endx,endy); cgcontextstrokepath(contextref); //hour hand angle = hourangle; endx = cos(angle) * (clockradius*0.65) + clockcentre.x; endy = sin(angle) * (clockradius*0.65) + clockcentre.y; cgcontextsetlinewidth(contextref, strokewidth*1.5); cgcontextmovetopoint(contextref,clockcentre.x,clockcentre.y); cgcontextaddlinetopoint(contextref,endx,endy); cgcontextstrokepath(contextref); } @end @interface viewcontroller () @end @implementation viewcontroller - (uiview*)drawclockfacewithcentre:(cgpoint)centre radius:(float)radius time:(nsdate*)time colour:(uicolor*)colour backgroundcolour:(uicolor*)bgcolour { uiview* clock = [[clock alloc]initwithcentre:centre radius:radius borderwidth:6.0 facecolour:bgcolour handcolour:colour time:time]; clock.backgroundcolor = [uicolor clearcolor]; return clock; } - (void)viewdidload { [super viewdidload]; // additional setup after loading view, typically nib. cgpoint centre; centre.x=200; centre.y=200; [self.view addsubview:[self drawclockfacewithcentre:centre radius:100 time:[nsdate date] colour:[uicolor blackcolor] backgroundcolour:[uicolor whitecolor]]]; } - (void)didreceivememorywarning { [super didreceivememorywarning]; // dispose of resources can recreated. } @end
i'm sure it's simple i've done wrong - mistake in maths. can see bug (and other suggestions improvements made welcome, bearing in mind (of course) simple test harness).
as wrote in comment, consider dropping doing in drawrect , use layers. also, instead of calculating trigonometry draw correct path, consider drawing hands @ 12:00 , transform rotate them correct angle. in init...
cashapelayer *facelayer = [cashapelayer layer]; facelayer.frame = self.bounds; facelayer.fillcolor = [uicolor whitecolor].cgcolor; facelayer.strokecolor = [uicolor blackcolor].cgcolor; facelayer.path = [uibezierpath bezierpathwithovalinrect:facelayer.bounds].cgpath; facelayer.linewidth = 3.0; [self.layer addsublayer:facelayer]; cgpoint middle = cgpointmake(cgrectgetmidx(self.bounds), cgrectgetmidy(self.bounds)); nscalendar *cal = [[nscalendar alloc] initwithcalendaridentifier:nscalendaridentifiergregorian]; nsuinteger units = nscalendarunithour | nscalendarunitminute | nscalendarunitsecond; nsdatecomponents *components = [cal components:units fromdate:time]; uibezierpath *path = [uibezierpath bezierpath]; [path movetopoint:middle]; [path addlinetopoint:cgpointmake(middle.x, middle.y-radius*0.5)]; cgfloat hourareaangle = (2*m_pi)/12.0; cashapelayer *hourlayer = [cashapelayer layer]; hourlayer.frame = self.bounds; hourlayer.strokecolor = [uicolor redcolor].cgcolor; hourlayer.linewidth = 3.0; hourlayer.path = path.cgpath; [self.layer addsublayer:hourlayer]; hourlayer.transform = catransform3dmakerotation((components.hour/12.0*(m_pi*2.0))+(hourareaangle*(components.minute/60.0)), 0.0, 0.0, 1.0); [path removeallpoints]; [path movetopoint:middle]; [path addlinetopoint:cgpointmake(middle.x, middle.y-radius*0.8)]; cashapelayer *minutelayer = [cashapelayer layer]; minutelayer.frame = self.bounds; minutelayer.strokecolor = [uicolor bluecolor].cgcolor; minutelayer.linewidth = 2.0; minutelayer.path = path.cgpath; [self.layer addsublayer:minutelayer]; minutelayer.transform = catransform3dmakerotation(components.minute/60.0*(m_pi*2.0), 0.0, 0.0, 1.0); [path removeallpoints]; [path movetopoint:middle]; [path addlinetopoint:cgpointmake(middle.x, middle.y-radius)]; cashapelayer *secondslayer = [cashapelayer layer]; secondslayer.frame = self.bounds; secondslayer.strokecolor = [uicolor greencolor].cgcolor; secondslayer.linewidth = 1.0; secondslayer.path = path.cgpath; [self.layer addsublayer:secondslayer]; secondslayer.transform = catransform3dmakerotation(components.second/60.0*(m_pi*2.0), 0.0, 0.0, 1.0);
note put colours , line widths because i'm lazy , didn't want check code :p.
if want update hands later, store hands layers variables , transform rotate them again (just remember first transform identity!).
Comments
Post a Comment