当前位置: 移动技术网 > 移动技术>移动开发>IOS > IOS上实现的自定义仪表盘示例

IOS上实现的自定义仪表盘示例

2019年07月24日  | 移动技术网移动技术  | 我要评论

今天给大家带来一个自定义的仪表盘,效果图如下。

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的下载地址:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网