IOS上实现的自定义仪表盘示例
程序员文章站
2024-02-16 22:09:04
今天给大家带来一个自定义的仪表盘,效果图如下。
demo中用到了 quartzcore类 首先继承一个uiview。
// gauge.h
// g...
今天给大家带来一个自定义的仪表盘,效果图如下。
demo中用到了 quartzcore类 首先继承一个uiview。
// gauge.h // gaugedemo // // created by 海锋 周 on 12-3-27. // copyright (c) 2012年 cjlu rights reserved. // #import <uikit/uikit.h> #import <quartzcore/quartzcore.h> @interface gauge : uiview { uiimage *gaugeview; uiimageview *pointer; cgfloat maxnum; cgfloat minnum; cgfloat maxangle; cgfloat minangle; cgfloat gaugevalue; cgfloat gaugeangle; cgfloat anglepervalue; cgfloat scolenum; nsmutablearray *labelarray; cgcontextref context; } @property (nonatomic,retain) uiimage *gaugeview; @property (nonatomic,retain) uiimageview *pointer; @property (nonatomic,retain) nsmutablearray *labelarray; @property (nonatomic) cgcontextref context; -(void)setgaugevalue:(cgfloat)value animation:(bool)isanim; @end
指针的旋转是通过quartzcore.framework中的catransform3drotate 来实现的,所以一定要记得把框架添加进来。当然在旋转之前,我们还需要把指针的中心pointer.layer.anchorpoint 移动到你需要的转动中心。
在设置旋转动画的时候,我们用的不是cabaseanimiation 而是用 cakeyframeanimation。这是因为如果使用中的 tovalue 来实现旋转的话,它默认是以最小的旋转的,如果要实现控制旋转的方向的话,我们就只能用关键帧来设置旋转的路径。用关键帧的好处还有一个,就是可以给指针添加,旋转到指定位置以后的左右摆动的效果。
绘制仪表盘是通过quartz2d来实现的,首先我们需要用uigraphicsgetcurrentcontext函数来获取一个context上下文,就是相当于获取一个画布。然后就可以在上面通过三角函数的计算,画出背景图片,和上面的刻度线了。
// gauge.m // gaugedemo // // created by 海锋 周 on 12-3-27. // copyright (c) 2012年 cjlu. all rights reserved. // #import "gauge.h" #import <quartzcore/quartzcore.h> #define maxoffsetangle 120.0f #define pointeroffset 90.0f #define maxvalue 120.0f #define cellmarknum 5 #define cellnum 12 #define gaugestring @"单位:km/h" #define defluatsize 300 /************************************************ 仪表盘的大小不建议设置的太小。 长宽都是300是最适合的 如果要更小的需要自行修改刻度长度和文字大小 ---powered by 周海锋 2012-3-29 ***********************************************/ @implementation gauge @interface gauge (private) - (cgfloat) parsetox:(cgfloat) radius angle:(cgfloat)angle; - (cgfloat) parsetoy:(cgfloat) radius angle:(cgfloat)angle; - (cgfloat) transtoradian:(cgfloat)angel; - (cgfloat) parsetoangle:(cgfloat) val; - (cgfloat) parsetovalue:(cgfloat) val; - (void)settextlabel:(nsinteger)labelnum; - (void)setlinemark:(nsinteger)labelnum; - (void) pointtoangle:(cgfloat) angle duration:(cgfloat) duration; @end @synthesize gaugeview,pointer,context; @synthesize labelarray; - (id)initwithframe:(cgrect)frame { self = [super initwithframe:frame]; if (self) { //设置背景透明 [self setbackgroundcolor:[uicolor clearcolor]]; scolenum = defluatsize/frame.size.width; maxnum = maxvalue; minnum = 0.0f; minangle = -maxoffsetangle; maxangle = maxoffsetangle; gaugevalue = 0.0f; gaugeangle = -maxoffsetangle; anglepervalue = (maxangle - minangle)/(maxnum - minnum); gaugeview= [uiimage imagenamed:@"gaugeback.png"]; //添加指针 uiimage *_pointer = [uiimage imagenamed:@"pointer2.png"]; pointer = [[uiimageview alloc] initwithimage:_pointer]; pointer.layer.anchorpoint = cgpointmake(0.5, 0.78); pointer.center = self.center; pointer.transform = cgaffinetransformmakescale(scolenum, scolenum); [self addsubview:pointer]; //设置文字标签 [self settextlabel:cellnum]; //设置指针到0位置 pointer.layer.transform = catransform3dmakerotation([self transtoradian:-maxoffsetangle], 0, 0, 1); } return self; } /* * settextlabel 绘制刻度值 * @labelnum nsinteger 刻度值的数目 */ -(void)settextlabel:(nsinteger)labelnum { labelarray = [nsmutablearray arraywithcapacity:labelnum]; cgfloat textdis = (maxnum - minnum)/labelnum; cgfloat angeldis = (maxangle - minangle)/labelnum; cgfloat radius = (self.center.x - 75)*scolenum; cgfloat currentangle; cgfloat currenttext = 0.0f; cgpoint centerpoint = self.center; for(int i=0;i<=labelnum;i++) { currentangle = minangle + i * angeldis - pointeroffset; currenttext = minnum + i * textdis; uilabel *label = [[uilabel alloc]initwithframe:cgrectmake(0 , 0 , 30, 50)]; label.autoresizessubviews = yes; label.textcolor = [uicolor whitecolor]; label.backgroundcolor = [uicolor clearcolor]; //设置刻度的文字的格式 if(i<labelnum/2){ label.textalignment = uitextalignmentleft; }else if (i==labelnum/2){ label.textalignment = uitextalignmentcenter; }else{ label.textalignment = uitextalignmentright; } label.text = [nsstring stringwithformat:@"%d",(int)currenttext]; label.center = cgpointmake(centerpoint.x+[self parsetox:radius angle:currentangle],centerpoint.y+[self parsetoy:radius angle:currentangle]); [labelarray addobject:label]; [self addsubview:label]; } // 设置刻度表的名称 uilabel *label = [[uilabel alloc]initwithframe:cgrectmake(0 , 0 ,100, 40)]; label.autoresizessubviews = yes; label.textcolor = [uicolor whitecolor]; label.backgroundcolor = [uicolor clearcolor]; label.textalignment = uitextalignmentcenter; label.text = gaugestring; label.center = cgpointmake(centerpoint.x,centerpoint.y*3/2); [self addsubview:label]; } /* * setlinemark 绘制刻度的标记 * @labelnum nsinteger 刻度是数目 */ -(void)setlinemark:(nsinteger)labelnum { cgfloat angeldis = (maxangle - minangle)/labelnum; cgfloat radius = self.center.x; cgfloat currentangle; cgpoint centerpoint = cgpointmake(self.frame.size.width/2, self.frame.size.height/2); for(int i=0;i<=labelnum;i++) { currentangle = minangle + i * angeldis - pointeroffset; //给刻度标记绘制不同的颜色 if(i>labelnum*2/3) { cgcontextsetstrokecolorwithcolor(context, [[uicolor colorwithred:1 green:0 blue:0 alpha:0.8] cgcolor]); }else if(i>labelnum*1/3){ cgcontextsetstrokecolorwithcolor(context, [[uicolor colorwithred:1 green:1 blue:0 alpha:0.8] cgcolor]); }else{ cgcontextsetstrokecolorwithcolor(context, [[uicolor colorwithred:0 green:1 blue:0 alpha:0.8] cgcolor]); } //绘制不同的长短的刻度 if(i%5==0) { cgcontextsetlinecap(context, kcglinecapsquare); cgcontextsetlinewidth(context, 3); cgcontextstrokepath(context); cgcontextmovetopoint(context,centerpoint.x+[self parsetox:radius-25*scolenum angle:currentangle], centerpoint.y+[self parsetoy:radius-25*scolenum angle:currentangle]); cgcontextaddlinetopoint(context,centerpoint.x+[self parsetox:radius-65*scolenum angle:currentangle], centerpoint.y+[self parsetoy:radius-65*scolenum angle:currentangle]); }else{ cgcontextsetlinewidth(context, 2); cgcontextsetlinecap(context, kcglinecapsquare); cgcontextstrokepath(context); cgcontextmovetopoint(context,centerpoint.x+[self parsetox:radius-25*scolenum angle:currentangle], centerpoint.y+[self parsetoy:radius-25*scolenum angle:currentangle]); cgcontextaddlinetopoint(context,centerpoint.x+[self parsetox:radius-40*scolenum angle:currentangle], centerpoint.y+[self parsetoy:radius-40*scolenum angle:currentangle]); } } } /* * setgaugevalue 移动到某个数值 * @value cgfloat 移动到的数值 * @isanim bool 是否执行动画 */ -(void)setgaugevalue:(cgfloat)value animation:(bool)isanim { cgfloat tempangle = [self parsetoangle:value]; gaugevalue = value; //设置转动时间和转动动画 if(isanim){ [self pointtoangle:tempangle duration:0.6f]; }else { [self pointtoangle:tempangle duration:0.0f]; } } /* * pointtoangle 按角度旋转 * @angel cgfloat 角度 * @duration cgfloat 动画执行时间 */ - (void) pointtoangle:(cgfloat) angle duration:(cgfloat) duration { cakeyframeanimation *anim=[cakeyframeanimation animationwithkeypath:@"transform"]; nsmutablearray *values=[nsmutablearray array]; anim.duration = duration; anim.autoreverses = no; anim.fillmode = kcafillmodeforwards; anim.removedoncompletion= no; cgfloat distance = angle/10; //设置转动路径,不能直接用 cabaseanimation 的tovalue,那样是按最短路径的,转动超过180度时无法控制方向 int i = 1; for(;i<=10;i++){ [values addobject:[nsvalue valuewithcatransform3d:catransform3drotate(catransform3didentity, [self transtoradian:(gaugeangle+distance*i)], 0, 0, 1)]]; } //添加缓动效果 [values addobject:[nsvalue valuewithcatransform3d:catransform3drotate(catransform3didentity, [self transtoradian:(gaugeangle+distance*(i))], 0, 0, 1)]]; [values addobject:[nsvalue valuewithcatransform3d:catransform3drotate(catransform3didentity, [self transtoradian:(gaugeangle+distance*(i-2))], 0, 0, 1)]]; [values addobject:[nsvalue valuewithcatransform3d:catransform3drotate(catransform3didentity, [self transtoradian:(gaugeangle+distance*(i-1))], 0, 0, 1)]]; anim.values=values; ; [pointer.layer addanimation:anim forkey:@"cubein"]; gaugeangle = gaugeangle+angle; } /* * parsetox 角度转弧度 * @angel cgfloat 角度 */ -(cgfloat)transtoradian:(cgfloat)angel { return angel*m_pi/180; } /* * parsetox 根据角度,半径计算x坐标 * @radius cgfloat 半径 * @angle cgfloat 角度 */ - (cgfloat) parsetox:(cgfloat) radius angle:(cgfloat)angle { cgfloat tempradian = [self transtoradian:angle]; return radius*cos(tempradian); } /* * parsetoy 根据角度,半径计算y坐标 * @radius cgfloat 半径 * @angle cgfloat 角度 */ - (cgfloat) parsetoy:(cgfloat) radius angle:(cgfloat)angle { cgfloat tempradian = [self transtoradian:angle]; return radius*sin(tempradian); } /* * parsetoangle 根据数据计算需要转动的角度 * @val cgfloat 要移动到的数值 */ -(cgfloat) parsetoangle:(cgfloat) val { //异常的数据 if(val<minnum){ return minnum; }else if(val>maxnum){ return maxnum; } cgfloat temp =(val-gaugevalue)*anglepervalue; return temp; } /* * parsetovalue 根据角度计算数值 * @val cgfloat 要移动到的角度 */ -(cgfloat) parsetovalue:(cgfloat) val { cgfloat temp=val/anglepervalue; cgfloat temp2=maxnum/2+temp; if(temp2>maxnum){ return maxnum; }else if(temp2<maxnum){ return maxnum; } return temp2; } - (void)drawrect:(cgrect)rect { //获取上下文 context = uigraphicsgetcurrentcontext(); //设置背景透明 cgcontextsetfillcolorwithcolor(context,self.backgroundcolor.cgcolor); cgcontextfillrect(context, rect); //绘制仪表背景 [[self gaugeview ]drawinrect:self.bounds]; //绘制刻度 [self setlinemark:cellnum*cellmarknum]; cgcontextstrokepath(context); } @end
demo的下载地址:http://xiazai.jb51.net/201701/yuanma/gaugedemo_jb51.rar
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。