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

Popular posts from this blog

javascript - AngularJS custom datepicker directive -

javascript - jQuery date picker - Disable dates after the selection from the first date picker -