1. 程式人生 > >iOS 餅狀圖的封裝與實現

iOS 餅狀圖的封裝與實現

ios 餅狀圖的封裝與實現

有時候我們在處理一些資料的時候,需要用到柱狀圖,折線圖和餅狀圖等來呈現資料,讓使用者能夠對資料更加清晰明瞭化。下面我們來看一下簡單的餅狀圖的實現。

延展

#import "NSObject+XuSong.h"
**NSObject+XuSong.h**
/**
 *  N秒後執行動作(不阻塞主執行緒)
 *
 *  @param seconds 幾秒
 *  @param actions 幾秒後執行的動作
 */
    - (void)dispatch_after_withSeconds:(float)seconds actions:(void(^)(void))actions;

**NSObject+XuSong.m**
- (void)dispatch_after_withSeconds:(float)seconds actions:(void(^)(void))actions{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(seconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    actions();
});

#import "NSString+XuSong.h"
**NSString+XuSong.h**
    /**
 *  計算字串寬度(指當該字串放在view時的自適應寬度)
 *
 *  @param size 填入預留的大小
 *  @param font 字型大小
 *
 *  @return 返回CGRect
 */
- (CGRect)stringWidthRectWithSize:(CGSize)size fontOfSize:(CGFloat)font;

**NSString+XuSong.m**
- (CGRect)stringWidthRectWithSize:(CGSize)size fontOfSize:(CGFloat)font{
NSDictionary * attributes = @{NSFontAttributeName: [UIFont boldSystemFontOfSize:font]};

return [self boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil];
}

#import "UIColor+XuSong.h"
**UIColor+XuSong.h**
@interface UIColor (XuSong)
@property (nonatomic, assign, readonly) CGFloat red;
@property (nonatomic, assign, readonly) CGFloat green;
@property (nonatomic, assign, readonly) CGFloat blue;
@property (nonatomic, assign, readonly) CGFloat alpha;
@end

**UIColor+XuSong.m**
@implementation UIColor (XuSong)
- (NSDictionary *)getRGBDictionaryByColor{
    CGFloat r=0,g=0,b=0,a=0;
    if ([self respondsToSelector:@selector(getRed:green:blue:alpha:)]) {
        [self getRed:&r green:&g blue:&b alpha:&a];
    }
    else {
        const CGFloat *components = CGColorGetComponents(self.CGColor);
        r = components[0];
        g = components[1];
        b = components[2];
        a = components[3];
    }

    r = r * 255;
    g = g * 255;
    b = b * 255;

    return @{@"R":@(r),
             @"G":@(g),
             @"B":@(b),
             @"A":@(a)};
}

- (CGFloat)red{
    NSDictionary * dict = [self getRGBDictionaryByColor];
    return [dict[@"R"] floatValue];
}

- (CGFloat)green{
    NSDictionary * dict = [self getRGBDictionaryByColor];
    return [dict[@"G"] floatValue];
}

- (CGFloat)blue{
    NSDictionary * dict = [self getRGBDictionaryByColor];
    return [dict[@"B"] floatValue];
}

- (CGFloat)alpha{
    NSDictionary * dict = [self getRGBDictionaryByColor];
    return [dict[@"A"] floatValue];
}
@end

#import "UIView+XuSong.h"
**UIView+XuSong.h**
/**
 *  自定義邊框
 *
 *  @param cornerRadius 角落半徑
 *  @param borderWidth  邊框寬度
 *  @param color        邊框顏色
 */
-(void)setBorderCornerRadius:(CGFloat)cornerRadius andBorderWidth:(CGFloat)borderWidth andBorderColor:(UIColor *)color;

**UIView+XuSong.m**
-(void)setBorderCornerRadius:(CGFloat)cornerRadius andBorderWidth:(CGFloat)borderWidth andBorderColor:(UIColor *)color{
self.layer.cornerRadius = cornerRadius;
self.layer.borderWidth = borderWidth;
self.layer.borderColor = color.CGColor;
}

標頭檔案

**ZFChart.h**
#import "ZFConst.h"
#import "ZFPieChart.h"
#import "ZFColor.h"
**ZFColor.h**
#define ZFBlack [UIColor blackColor]
#define ZFDarkGray [UIColor darkGrayColor]
#define ZFLightGray [UIColor lightGrayColor]
#define ZFWhite [UIColor whiteColor]
#define ZFGray [UIColor grayColor]
#define ZFRed [UIColor redColor]
#define ZFGreen [UIColor greenColor]
#define ZFBlue [UIColor blueColor]
#define ZFCyan [UIColor cyanColor]
#define ZFYellow [UIColor yellowColor]
#define ZFMagenta [UIColor magentaColor]
#define ZFOrange [UIColor orangeColor]
#define ZFPurple [UIColor purpleColor]
#define ZFBrown [UIColor brownColor]
#define ZFClear [UIColor clearColor]
**ZFConst.h**
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
#define ADAPTATION_WIDTH7(Width) [UIScreen mainScreen].bounds.size.width * (Width) / 375
#define IMGNAME(name) [UIImage imageNamed:name]
/**
 *  直接填寫小數
 */
#define ZFDecimalColor(r, g, b, a) [UIColor colorWithRed:r green:g blue:b alpha:a]

/**
 *  直接填寫整數
 */
#define ZFColor(r, g, b, a) [UIColor colorWithRed:r / 255.f green:g / 255.f blue:b / 255.f alpha:a]

/**
 *  隨機顏色
 */
#define ZFRandomColor ZFColor(arc4random() % 256, arc4random() % 256, arc4random() % 256, 1)

#define NAVIGATIONBAR_HEIGHT 64.f
#define TABBAR_HEIGHT 49.f

/**
 *  角度求三角函式sin值
 *  @param a 角度
 */
#define ZFSin(a) sin(a / 180.f * M_PI)

/**
 *  角度求三角函式cos值
 *  @param a 角度
 */
#define ZFCos(a) cos(a / 180.f * M_PI)

/**
 *  角度求三角函式tan值
 *  @param a 角度
 */
#define ZFTan(a) tan(a / 180.f * M_PI)

/**
 *  弧度轉角度
 *  @param radian 弧度
 */
#define ZFAngle(radian) (radian / M_PI * 180.f)

/**
 *  角度轉弧度
 *  @param angle 角度
 */
#define ZFRadian(angle) (angle / 180.f * M_PI)

/**
 *  座標軸起點x值
 */
#define ZFAxisLineStartXPos 50.f

/**
 *  y軸label tag值
 */
#define YLineValueLabelTag 100

/**
 *  x軸item寬度
 */
#define XLineItemWidth 25.f

/**
 *  x軸item間隔
 */
#define XLineItemGapLength 20.f


#warning message - 此屬性最好不要隨意修改
/**
 *  座標y軸最大上限值到箭頭的間隔距離 (此屬性最好不要隨意修改)
 */
#define ZFAxisLineGapFromYLineMaxValueToArrow 20.f

畫線和動畫效果

#import “ZFTranslucencePath.h”

**ZFTranslucencePath.h**
    #import <QuartzCore/QuartzCore.h>
    #import <UIKit/UIKit.h>

    @interface ZFTranslucencePath : CAShapeLayer

    + (instancetype)layerWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;

    - (instancetype)initWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise;
    @end

    **ZFTranslucencePath.m**
    @implementation ZFTranslucencePath

    + (instancetype)layerWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise{
        return [[ZFTranslucencePath alloc] initWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:clockwise];
    }

    - (instancetype)initWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise{
        self = [super init];
        if (self) {
            self.fillColor = nil;
            self.opacity = 0.5f;
            self.path = [self translucencePathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:clockwise].CGPath;
        }
        return self;
    }

    - (UIBezierPath *)translucencePathWithArcCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise{
        UIBezierPath * bezierPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:clockwise];
        return bezierPath;
    }
    @end

主檢視

#import “ZFPieChart.h”

**ZFPieChart.h**

#import <UIKit/UIKit.h>
typedef enum{
    /**
     *  保留2位小數形式(預設)
     */
    kPercentTypeDecimal = 0,
    /**
     *  取整數形式(四捨五入)
     */
    kPercentTypeInteger = 1
}kPercentType;

@interface ZFPieChart : UIView

/** 標題 */
@property (nonatomic, copy) NSString * title;
/** 數值陣列 (儲存的是NSString型別) */
@property (nonatomic, strong) NSMutableArray * valueArray;
/** 名字陣列 (儲存的是NSString型別) */
@property (nonatomic, strong) NSMutableArray * nameArray;
/** 顏色陣列 (儲存的是UIColor型別) */
@property (nonatomic, strong) NSMutableArray * colorArray;
/** kPercentType型別 */
@property (nonatomic, assign) kPercentType percentType;
/** 顯示詳細資訊(預設為YES) */
@property (nonatomic, assign) BOOL isShowDetail;
/** 顯示百分比(預設為YES) */
@property (nonatomic, assign) BOOL isShowPercent;

#pragma mark - public method

/**
 *  重繪
 */
- (void)strokePath;

@end

**ZFPieChart.m**

#import "ZFPieChart.h"
#import "ZFConst.h"
#import "NSObject+XuSong.h"
#import "NSString+XuSong.h"
#import "UIColor+XuSong.h"
#import "UIView+XuSong.h"
#import "ZFTranslucencePath.h"
#import "Masonry.h"

#define PercentLabelTag 100
#define DetailBackgroundTag 500

@interface ZFPieChart()

/** 總數 */
@property (nonatomic, assign) CGFloat totalValue;
/** 半徑 */
@property (nonatomic, assign) CGFloat radius;
/** 半徑最大上限 */
@property (nonatomic, assign) CGFloat maxRadius;
/** 記錄每個圓弧開始的角度 */
@property (nonatomic, assign) CGFloat startAngle;
/** 動畫總時長 */
@property (nonatomic, assign) CFTimeInterval totalDuration;
/** 圓環線寬 */
@property (nonatomic, assign) CGFloat lineWidth;
/** 記錄valueArray當前元素的下標 */
@property (nonatomic, assign) NSInteger index;
/** 記錄當前path的中心點 */
@property (nonatomic, assign) CGPoint centerPoint;
/** 半透明Path延伸長度 */
@property (nonatomic, assign) CGFloat extendLength;
/** 記錄圓環中心 */
@property (nonatomic, assign) CGPoint pieCenter;
/** 記錄初始高度 */
@property (nonatomic, assign) CGFloat originHeight;
/** 儲存每個圓弧動畫開始的時間 */
@property (nonatomic, strong) NSMutableArray * startTimeArray;
/** 記錄每個path startAngle 和 endAngle, 數組裡存的是NSDictionary */
@property (nonatomic, strong) NSMutableArray * angelArray;
/** 標題Label */
@property (nonatomic, strong) UILabel * titleLabel;
/** 數值Label */
@property (nonatomic, strong) UILabel * valueLabel;

@end

@implementation ZFPieChart

- (NSMutableArray *)startTimeArray{
    if (!_startTimeArray) {
        _startTimeArray = [NSMutableArray array];
    }
    return _startTimeArray;
}

- (NSMutableArray *)angelArray{
    if (!_angelArray) {
        _angelArray = [NSMutableArray array];
    }
    return _angelArray;
}

/**
 *  初始化變數
 */
- (void)commonInit{
    _maxRadius = self.frame.size.width > self.frame.size.height ? self.frame.size.height : self.frame.size.width;
    _radius = _maxRadius * 0.27;
    _lineWidth = _radius;
    _totalDuration = 0.75f;
    _startAngle = ZFRadian(-90);
    _extendLength = 10.f;
    _originHeight = self.frame.size.height;
    _pieCenter = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2);
    _isShowDetail = YES;
    _isShowPercent = YES;
}

- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        [self commonInit];

        //數值Label
        self.valueLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, _radius/1.4, _radius/1.4)];
        self.valueLabel.font = [UIFont boldSystemFontOfSize:13.f];
        self.valueLabel.textAlignment = NSTextAlignmentCenter;
        self.valueLabel.textColor = [UIColor blackColor];
        self.valueLabel.numberOfLines = 0;
        self.valueLabel.center = self.pieCenter;
        [self addSubview:self.valueLabel];
    }
    return self;
}

/**
 *  新增詳情
 */
- (void)addUI{
    CGFloat valueMoney = 0.0;
    for (NSInteger i = 0; i < self.valueArray.count; i++) {
        //裝載容器
        UIView * background = [[UIView alloc] initWithFrame:CGRectMake(0, self.frame.size.height + ADAPTATION_HEIGHT7(50) * i, self.frame.size.width, ADAPTATION_HEIGHT7(50))];
        background.tag = DetailBackgroundTag + i;
        [self addSubview:background];

        UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(showTranslucencePathAction:)];
        [background addGestureRecognizer:tap];

        //線條
        UIView *lineView = [[UIView alloc] init];
        lineView.backgroundColor = [UIColor lightGrayColor];
        [background addSubview:lineView];
        [lineView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(background);
            make.left.equalTo(background).offset(10);
            make.right.equalTo(background);
            make.height.mas_offset(1);
        }];
        UIImageView *colorImage = [[UIImageView alloc] init];
        colorImage.image = IMGNAME(_nameArray[i]);
        [background addSubview:colorImage];
        [colorImage mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerY.equalTo(background);
            make.left.equalTo(background).offset(20);
            make.size.mas_equalTo(CGSizeMake(ADAPTATION_WIDTH7(35), ADAPTATION_WIDTH7(35)));
        }];

        //名稱
        UILabel *nameLabel = [[UILabel alloc] init];
        nameLabel.text = _nameArray[i];
        nameLabel.font = [UIFont boldSystemFontOfSize:18];
        nameLabel.textAlignment = NSTextAlignmentLeft;
        [background addSubview:nameLabel];
        [nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerY.equalTo(background);
            make.left.equalTo(colorImage.mas_right).offset(15);
            make.size.mas_equalTo(CGSizeMake(60, 30));
        }];

        //數值
        UILabel *valueLabel = [[UILabel alloc] init];
        valueLabel.font = [UIFont systemFontOfSize:16];
        valueLabel.text = [NSString stringWithFormat:@"%@元",_valueArray[i]];
        valueLabel.textAlignment = NSTextAlignmentCenter;
        [background addSubview:valueLabel];
        [valueLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerX.equalTo(background);
            make.centerY.equalTo(background);
            make.size.mas_equalTo(CGSizeMake(150, 30));
        }];
        valueMoney += [_valueArray[i] floatValue];
        self.valueLabel.text = [NSString stringWithFormat:@"總金額%.2f",valueMoney];
        //百分比
        UILabel *percentLabel = [[UILabel alloc] init];
        percentLabel.text = [self getPercent:i];
        percentLabel.font = [UIFont systemFontOfSize:16];
        percentLabel.textAlignment = NSTextAlignmentRight;
        [background addSubview:percentLabel];
        [percentLabel mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerY.equalTo(background);
            make.right.equalTo(background).offset(-15);
            make.size.mas_equalTo(CGSizeMake(80, 30));
        }];
    }

    //重設self.frame的值
    UILabel * lastLabel = (UILabel *)[self viewWithTag:DetailBackgroundTag + self.valueArray.count - 1];
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, CGRectGetMaxY(lastLabel.frame) + 20);
}

#pragma mark - Arc(圓弧)

/**
 *  填充
 *
 *  @return UIBezierPath
 */
- (UIBezierPath *)fill{
    //需要多少度的圓弧
    CGFloat angle = [self countAngle:[_valueArray[_index] floatValue]];

    UIBezierPath * bezier = [UIBezierPath bezierPathWithArcCenter:_pieCenter radius:_radius startAngle:_startAngle endAngle:_startAngle + angle clockwise:YES];
    self.centerPoint = [self getBezierPathCenterPointWithStartAngle:_startAngle endAngle:_startAngle + angle];
    //記錄開始角度和結束角度
    NSDictionary * dict = @{@"startAngle":@(_startAngle), @"endAngle":@(_startAngle + angle)};
    [self.angelArray addObject:dict];

    _startAngle += angle;

    return bezier;
}

/**
 *  CAShapeLayer
 *
 *  @return CAShapeLayer
 */
- (CAShapeLayer *)shapeLayer{
    CAShapeLayer * layer = [CAShapeLayer layer];
    layer.fillColor = nil;
    layer.strokeColor = [_colorArray[_index] CGColor];
    layer.lineWidth = _lineWidth;
    layer.path = [self fill].CGPath;

    CABasicAnimation * animation = [self animation];
    [layer addAnimation:animation forKey:nil];

    return layer;
}

#pragma mark - 動畫

/**
 *  填充動畫過程
 *
 *  @return CABasicAnimation
 */
- (CABasicAnimation *)animation{
    CABasicAnimation * fillAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    fillAnimation.duration = [self countDuration:_index];
    fillAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    fillAnimation.fillMode = kCAFillModeForwards;
    fillAnimation.removedOnCompletion = NO;
    fillAnimation.fromValue = @(0.f);
    fillAnimation.toValue = @(1.f);

    return fillAnimation;
}

#pragma mark - 清除控制元件

/**
 *  清除之前所有子控制元件
 */
- (void)removeAllSubLayers{
    [self.angelArray removeAllObjects];
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, _originHeight);

    NSArray * subLayers = [NSArray arrayWithArray:self.layer.sublayers];
    for (CALayer * layer in subLayers) {
        if (layer != self.titleLabel.layer && layer != self.valueLabel.layer) {
            [layer removeAllAnimations];
            [layer removeFromSuperlayer];
        }
    }

    for (UIView * view in self.subviews) {
        if (view != self.titleLabel && view != self.valueLabel) {
            [view removeFromSuperview];
        }
    }
}

/**
 *  移除半透明Path
 */
- (void)removeZFTranslucencePath{
    NSMutableArray * sublayers = [NSMutableArray arrayWithArray:self.layer.sublayers];
    for (CALayer * layer in sublayers) {
        if ([layer isKindOfClass:[ZFTranslucencePath class]]) {
            [layer removeFromSuperlayer];
        }
    }
}

#pragma mark - 半透明Path

/**
 *  半透明Path
 *
 *  @param startAngle 開始角度
 *  @param endAngle   結束角度
 *  @param index      下標
 *
 *  @return ZFTranslucencePath
 */
- (ZFTranslucencePath *)translucencePathShapeLayerWithStartAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle index:(NSInteger)index{
    ZFTranslucencePath * layer = [ZFTranslucencePath layerWithArcCenter:_pieCenter radius:_radius + _extendLength startAngle:startAngle endAngle:endAngle clockwise:YES];
    layer.strokeColor = [_colorArray[index] CGColor];
    layer.lineWidth = _lineWidth + _extendLength;
    return layer;
}

#pragma mark - public method

/**
 *  重繪
 */
- (void)strokePath{
    self.userInteractionEnabled = NO;
    [self removeAllSubLayers];
    _startAngle = ZFRadian(-90);

    for (NSInteger i = 0; i < _valueArray.count; i++) {
        [self dispatch_after_withSeconds:[self.startTimeArray[i] floatValue] actions:^{
            _index = i;
            CAShapeLayer * shapeLayer = [self shapeLayer];
            [self.layer addSublayer:shapeLayer];
            _isShowPercent == YES?[self creatPercentLabel]:nil;
        }];
    }

    [self dispatch_after_withSeconds:_totalDuration actions:^{
        self.userInteractionEnabled = YES;
    }];

    _isShowDetail == YES?[self addUI]:nil;
}

#pragma mark - UIResponder

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    UITouch * touch = [touches anyObject];
    CGPoint point = [touch locationInView:self];
    if (point.y > _originHeight / 8.f * 7 + 30) {
        return;
    }

    //求弧度
    CGFloat x = (point.x - _pieCenter.x);
    CGFloat y = (point.y - _pieCenter.y);
    CGFloat radian = atan2(y, x);
    //當超過180度時,要加2π
    if (y < 0 && x < 0) {
        radian = radian + ZFRadian(360);
    }

    //判斷點選位置的角度在哪個path範圍上
    for (NSInteger i = 0; i < self.angelArray.count; i++) {
        NSDictionary * dict = self.angelArray[i];
        CGFloat startAngle = [dict[@"startAngle"] floatValue];
        CGFloat endAngle = [dict[@"endAngle"] floatValue];

        if (radian >= startAngle && radian < endAngle) {
            [self removeZFTranslucencePath];
            [self.layer addSublayer:[self translucencePathShapeLayerWithStartAngle:startAngle endAngle:endAngle index:i]];
            UILabel * percentLabel = [self viewWithTag:PercentLabelTag + i];
            [self bringSubviewToFront:percentLabel];
            self.valueLabel.text = _valueArray[i];

            return;
        }
    }
}

/**
 *  顯示半透明Path Action
 *
 *  @param sender UITapGestureRecognizer
 */
- (void)showTranslucencePathAction:(UITapGestureRecognizer *)sender{
    NSInteger index = sender.view.tag - DetailBackgroundTag;
    NSDictionary * dict = self.angelArray[index];
    CGFloat startAngle = [dict[@"startAngle"] floatValue];
    CGFloat endAngle = [dict[@"endAngle"] floatValue];

    [self removeZFTranslucencePath];
    [self.layer addSublayer:[self translucencePathShapeLayerWithStartAngle:startAngle endAngle:endAngle index:index]];
    UILabel * percentLabel = [self viewWithTag:PercentLabelTag + index];
    [self bringSubviewToFront:percentLabel];
    self.valueLabel.text = _valueArray[index];
}

#pragma mark - 獲取每個item所佔百分比

/**
 *  計算每個item所佔角度大小
 *
 *  @param value 每個item的value
 *
 *  @return 返回角度大小
 */
- (CGFloat)countAngle:(CGFloat)value{
    //計算百分比
    CGFloat percent = value / _totalValue;
    //需要多少度的圓弧
    CGFloat angle = M_PI * 2 * percent;
    return angle;
}

#pragma mark - 計算每個圓弧執行動畫持續時間

/**
 *  計算每個圓弧執行動畫持續時間
 *
 *  @param index 下標
 *
 *  @return CFTimeInterval
 */
- (CFTimeInterval)countDuration:(NSInteger)index{
    if (_totalDuration < 0.1f) {
        _totalDuration = 0.1f;
    }
    float count = _totalDuration / 0.1f;
    CGFloat averageAngle =  M_PI * 2 / count;
    CGFloat time = [self countAngle:[_valueArray[index] floatValue]] / averageAngle * 0.1;

    return time;
}

#pragma mark - 獲取每個path的中心點

/**
 *  獲取每個path的中心點
 *
 *  @return CGFloat
 */
- (CGPoint)getBezierPathCenterPointWithStartAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle{
    //一半形度(弧度)
    CGFloat halfAngle = (endAngle - startAngle) / 2;
    //中心角度(弧度)
    CGFloat centerAngle = halfAngle + startAngle;
    //中心角度(角度)
    CGFloat realAngle = ZFAngle(centerAngle);

    CGFloat center_xPos = ZFCos(realAngle) * _radius + _pieCenter.x;
    CGFloat center_yPos = ZFSin(realAngle) * _radius + _pieCenter.y;

    return CGPointMake(center_xPos, center_yPos);
}

#pragma mark - 新增百分比Label

/**
 *  新增百分比Label
 */
- (void)creatPercentLabel{
    NSString * string = [self getPercent:_index];
    CGRect rect = [string stringWidthRectWithSize:CGSizeMake(0, 0) fontOfSize:9.f];

    UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, rect.size.width, rect.size.height)];
    if ([string isEqualToString:@"0.00%"]) {
        label.text = @"";
    }else {
        label.text = string;
    }
    label.alpha = 0.f;
    label.textAlignment = NSTextAlignmentCenter;
    label.font = [UIFont boldSystemFontOfSize:9.f];
    label.center = self.centerPoint;
    label.tag = PercentLabelTag + _index;
    [self addSubview:label];

    [UIView animateWithDuration:[self countDuration:_index] animations:^{
        label.alpha = 1.f;
    }];

    //獲取r,g,b三色值
    CGFloat red = [_colorArray[_index] red];
    CGFloat green = [_colorArray[_index] green];
    //path顏色為深色時,更改文字顏色
    if ((red < 180.f && green < 180.f)) {
        label.textColor = [UIColor whiteColor];
    }
}

/**
 *  計算百分比
 *
 *  @return NSString
 */
- (NSString *)getPercent:(NSInteger)index{
    CGFloat percent = [_valueArray[index] floatValue] / _totalValue * 100;
    NSString * string;
    if (self.percentType == kPercentTypeDecimal) {
        string = [NSString stringWithFormat:@"%.2f%%",percent];
    }else if (self.percentType == kPercentTypeInteger){
        string = [NSString stringWithFormat:@"%d%%",(int)roundf(percent)];
    }
    return string;
}

#pragma mark - 重寫setter,getter方法

- (void)setValueArray:(NSMutableArray *)valueArray{
    _valueArray = valueArray;
    _totalValue = 0;
    [self.startTimeArray removeAllObjects];
    CFTimeInterval startTime = 0.f;
    //計算總數
    for (NSInteger i = 0; i < valueArray.count; i++) {
        _totalValue += [valueArray[i] floatValue];
    }

    //計算每個path的開始時間
    for (NSInteger i = 0; i < valueArray.count; i++) {
        [self.startTimeArray addObject:[NSNumber numberWithDouble:startTime]];
        CFTimeInterval duration = [self countDuration:i];
        startTime += duration;
    }
}

@end

餅狀圖效果

#import “ViewController.h”

**#import "ViewController.h"**
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController

@end

**#import "ViewController.m"**

#import "ViewController.h"
#import "ZFChart.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    ZFPieChart *pieChart = [[ZFPieChart alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_WIDTH)];
    pieChart.valueArray = [NSMutableArray arrayWithArray:@"410", @"510", @"380", @"420", @"260",nil];
pieChart.nameArray = [NSMutableArray arrayWithObjects:@"購物", @"美食", @"住房", @"交通", @"娛樂", nil];
pieChart.colorArray = [NSMutableArray arrayWithObjects:ZFColor(253, 118, 152, 1), ZFColor(254, 223, 219, 1), ZFColor(254, 206, 103, 1), ZFColor(81, 146, 218, 1), ZFColor(112, 182, 146, 1), nil];
[self.view addSubview:pieChart];
[self.pieChart strokePath];

}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end

效果圖

這裡寫圖片描述
這裡寫圖片描述

餅狀圖效果圖。本文的Demo是借鑑自網上,非博主純原創。敬請諒解。

相關推薦

iOS 封裝實現

ios 餅狀圖的封裝與實現 有時候我們在處理一些資料的時候,需要用到柱狀圖,折線圖和餅狀圖等來呈現資料,讓使用者能夠對資料更加清晰明瞭化。下面我們來看一下簡單的餅狀圖的實現。 延展 #import "NSObject+XuSong.h" **NSOb

java代碼實現highchart數據庫數據結合完整案例分析(一)---

隱藏 des log cred 數據庫數據 idt string 時間 input 作者原創:轉載請註明出處 在做項目的過程中,經常會用到統計數據,同時會用到highchart或echart進行數據展示,highchart是外國開發的數據統計圖插件, echa

echarts實現一個頁面多個共用方法的封裝

var warnStates = (function(){ var warnNumCount = warnNum; var joinAllManCount = joinAllMan; return { warnStatesData : function(){ v

ECharts 報表事件聯動系列三:柱實現聯動

餅狀圖 img and int js函數 radius func get 執行 源碼如下: <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type

js(帶百分比)功能實現,新人必懂

tool edi state pro form allow UNC connect ajax 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8

Qt文件閱讀筆記-C++QML混合程式設計(QML畫

這裡只點名一點: Qt Charts是利用Qt的圖形檢視框架(QGraphics)搞出來的,底層並不是用OPenGL,而QML卻與OPenGL的底層緊密關聯 執行截圖如下: 原始碼如下

iOS開發之資訊類App常用分類控制元件的封裝實現(CollectionView+Swift3.0+)

今天部落格中,我們就來實現一下一些常用資訊類App中常用的分類選擇的控制元件的封裝。本篇部落格中沒有使用到什麼新的技術點,如果非得說用到了什麼新的技術點的話,那麼勉強的說,用到了一些iOS9以後UICollectionView新增的一些新的特性。本篇部落格所涉及的技術點主要有UICollectionView的

原生js和canvas實現的 柱、折線

轉載來自https://blog.csdn.net/u013302113/article/details/77985744 <html> <head lang="en"> </head> <body> &

使用Highcharts結合PHPMysql生成

我們在做複雜的資料統計功能時會用到餅狀圖,餅狀圖用整個圓表示總體的數量或整體值1,用圓內各個扇形的大小表示各部分數量或該部分佔總體的百分比,它可以清晰直觀的表示各部分之間以及各部分與整體之間的數量關係。 本文將結合實際,使用PHP讀取Mysql資料表中的資料,並將獲取

iOS仿支付寶賬單

前言: 這段時間專案做了一個賬單查詢的頁面使用到了餅狀圖,支付寶賬單那個餅狀圖,就簡單的封裝了一個給大家看看,使用的基本技術也就是使用UIBezierPath繪製柱狀圖路徑,再把CAShapeLayer和UIBezierPath建立關係,最後使用CABasicAnima

jQuery圖表外掛 jqPlot實現

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html lang="en

iOS使用Charts框架繪製—

首先先看一下效果: 餅狀圖 一、建立餅狀圖物件 建立餅狀圖物件用到類是PieChartView.h, 程式碼如下: self.pieChartView = [[PieChartView alloc] init]; self.pieChartView.backgroundC

基於SpringMVC框架使用ECharts3.0實現折線,柱,的繪製

頁面部分:<%@ page language="java" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> &l

(PieChart)柱形(BarChart)的使用

最近在工作中需要用到餅狀圖的功能,網上查了一下MPAndroidChart開源圖表庫是一個很好的東西,並下載了MPAndroidChart專案執行。於是自己寫了一個簡單的例子,使用PieChart(餅圖)的方法如下: 原始碼下載地址:https://githu

Android開發自定義控制元件實現一個

實現一個如圖所示的控制元件,包括兩部分,左邊的餅狀圖和中間的兩個小方塊,及右邊的兩行文字 實現起來比較簡單,只是一些繪圖API的呼叫 核心程式碼在onDraw函式裡邊,,對靜態控制元件進行繪製即可 @Override protected void onDraw(Canv

使用css3制作正方形、三角形、扇形和

radi spa over pointer tran ima 得到 lin 引入 1.利用邊框制作正方形 如果將盒容器的width和height設置為0,並為每條邊設置一個較粗的width值和彼此不同的顏色,最終會得到四個被拼接在一起三角形,它們分別指向不同的顏色。 htm

JavaScript+svg繪制的一個

圖例 n) attribute 數字類型 XML 用戶 h+ htm type svg參考:https://www.w3.org/TR/SVG/<body onload=‘document.body.appendChild( pieChart([12,23,34

PHP畫矩形,橢圓,圓,畫橢圓弧 ,

tro lips ade inpu 統計 起點 com eth func 1:畫矩形: imagerectangle ( resource $image , int $x1 , int $y1 , int $x2 , int $y2 , int $col ) imagere

柱形JavaScript

column 註意 文件 label max number span item pointf <script type="text/javascript"> $(function () { $(‘#container_2‘).highcharts({

R圖表_

legend 之間 lock 圖標 圖片 explode nds percent 統計 R編程語言中有許多庫用來創建圖表。餅狀圖是以不同顏色的圓的切片表示的值。這些切片被標記,並且每個切片對應的數字也在圖表中表示。 在R中,使用將正數作為向量輸入的pie()函數創建餅狀圖。