HardBirch

【IOS】IOS上实现的自定义仪表盘(可以用在流量监控之类的应用上)

时间:12-03-29 栏目:iOS游戏开发技术 作者:张飞不张,文采横飞 评论:5 点击: 3,890 次

 

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

 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://download.csdn.net/detail/toss156/4183721

【IOS】IOS上实现的自定义仪表盘(可以用在流量监控之类的应用上):目前有5 条留言

  1. 5楼
    dongge_111:

    不错

    2012-03-29 18:43 [回复]
  2. 4楼
    szhou28:

    mark,不错,多谢了啊。

    2012-05-08 17:26 [回复]
  3. 地板
    fengDota:

    非常好,感谢你的慷慨分享,让我受益匪浅啊 泪奔中…..

    2012-05-31 15:10 [回复]
  4. 板凳
    tackwin:

    非常不错,学到了很多东西。

    2012-08-25 14:54 [回复]
  5. 沙发
    oLingFeng124:

    正是我想找的东西 非常感谢

    2012-10-29 14:33 [回复]

发表评论


QQ群互动

Linux系统与内核学习群:194051772

WP建站技术学习交流群:194062106

魔豆之路QR

魔豆的Linux内核之路

魔豆的Linux内核之路

优秀工程师当看优秀书籍

优秀程序员,要看优秀书!

赞助商广告

友荐云推荐