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

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

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

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

(0)

相关推荐

  • iOS仿支付宝芝麻信用分数仪表盘动画效果

    先看看效果图: 仪表盘动画效果.jpg 1.圆环上绿点的旋转 2.分数值及提示语的变化 3.背景色的变化 直接上主要代码: 1.自定义ZLDashboardView仪表盘文件: .h 文件: /** * 根据跃动数字 * * 确定百分比 * 现在的跳动数字-->背景颜色变化 * */ #import <UIKit/UIKit.h> @interface ZLDashboardView : UIView @property (nonatomic, strong) UIImage *bgIm

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

    今天给大家带来一个自定义的仪表盘,效果图如下. 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 : U

  • iOS Swift开发之日历插件开发示例

    本文介绍了iOS Swift开发之日历插件开发示例,分享给大家,具体如下: 效果图 0x01 如何获取目前日期 关于日期,苹果给出了 Date 类,初始化一个 Date 类 let date = Date() 打印出来就是当前系统的日期和时间 那么如何单独获得当前年份,月份呢? var date: [Int] = [] let calendar: Calendar = Calendar(identifier: .gregorian) var comps: DateComponents = Dat

  • Android实现史上最简单自定义开关按钮的方法

    目录 前言 一.原理 二.实现 1.自定义View类MyToggle 1)属性字段 2)覆写View类的构造方法 3)创建init方法 4)手指触摸事件回调方法onTouch 5)界面重绘方法onDraw 6)计算开关的宽高 7)设置图片资源信息 8)设置开关按钮的状态 9)自定义开关状态监听器 10)设置开关监听器 11)MyToggle完整代码如下: 2.MainActivity 3.布局文件activity_main.xml 4.AndroidManifest.xml 三.运行效果 四.温

  • iOS利用MJRefresh实现自定义刷新动画效果

    本文主要介绍iOS 利用MJRefresh实现自定义动画的上拉刷新下拉加载效果,一般的类型(包括更新时间与loading图案)这里不做介绍. 要想实现此功能,首先得有一套load的图片数组. 接下来就是实现过程: 引入头文件: #import "MJRefresh.h" //自定义一个方法实现 - (void)prepareRefresh { NSMutableArray *headerImages = [NSMutableArray array]; for (int i = 1; i

  • .NetCore实现上传多文件的示例详解

    本章和大家分享的是.NetCore的MVC框架上传文件的示例,主要讲的内容有:form方式提交上传,ajax上传,ajax提交+上传进度效果,Task并行处理+ajax提交+上传进度,相信当你读完文章内容后能后好的收获,如果可以不妨点个赞:由于昨天电脑没电了,快要写完的内容没有保存,今天早上提前来公司从头开始重新,断电这情况的确让人很头痛啊,不过为了社区的分享环境,这也是值得的,不多说了来进入今天的正篇环节吧: form方式上传一组图片 先来看看咋们html的代码,这里先简单说下要上传文件必须要

  • 文件上传的几个示例分享【推荐】

    本篇将要和朋友们分享的是几个上传文件的例子和逻辑步奏及自定义个简单的js上传插件我取名为shenniu.upfile-0.0.1.js:近来在讨论组中很有几个朋友咨询上传文件的代码和怎么下载上传的文件,所以写了此篇文章,希望能为朋友们解答一些疑惑或能带来帮助,也谢谢各位多多支持点赞. 以上是个人的看法,下面来正式分享今天的文章吧: 使用iis发布保存上传文件的文件夹 示例A - 普通表单上传文件,Request.Files获取上传文件 示例B - 普通表单上传文件,HttpPostedFileB

  • IOS实现百度地图自定义大头针和气泡样式

    一.自定义大头针和气泡 // 根据anntation生成对应的View - (BMKAnnotationView *)mapView:(BMKMapView *)mapView viewForAnnotation:(id <BMKAnnotation>)annotation { NSString *AnnotationViewID = [NSString stringWithFormat:@"renameMark%d",i]; newAnnotation = [[BMKPi

  • Android仿IOS上拉下拉弹性效果的实例代码

    用过iphone的朋友相信都体验过页面上拉下拉有一个弹性的效果,使用起来用户体验很好:Android并没有给我们封装这样一个效果,我们来看下在Android里如何实现这个效果.先看效果,感觉有些时候还是蛮实用的. 思路:其实原理很简单,实现一个自定义的Scrollview方法(来自网上大神),然后在布局文件中使用自定义方法Scrollview就可以了. 代码: 自定义View,继承自Scrollview.MyReboundScrollView类 package com.wj.myrebounds

  • Android ListView 实现上拉加载的示例代码

    本文介绍了Android ListView 实现上拉加载的示例代码,分享给大家,具体如下: 我们先分析一下如何实现 ListView 上拉加载. 当我们上拉的时候,会出现一个提示界面,即 ListView 的 Footer 布局. ListView 要实现滚动,所以要监听 ListView 滚动事件,即 OnScrollListener() 事件. 当我们开始滚动时,Footer 布局才慢慢显示出来,所以需要监听 ListView 的 onTouch() 事件. 实现思路 首先判断 ListVi

  • vue2.0自定义指令示例代码详解

    1.什么是指令? 指令通常以"v-"作为前缀, 以方便Vue知道你在使用一种特殊的标记. 除了 Vue 核心携带着的一些默认指令(v-model 和 v-show)之外, Vue 还允许你注册自己的自定义指令.某些情况下,还是需要对普通元素进行一些底层 DOM 访问, 这也是自定义指令仍然有其使用场景之处. 2.全局指令: 当页面加载时,元素将获取焦点,事实上,在访问页面时,如果你还没有点击任何地方,上面的输入框现在应该处于获取焦点的状态.现在让我们构建指令以完成此效果: <te

随机推荐