欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

iOS 饼状图的封装与实现

程序员文章站 2022-06-08 17:49:05
有时候我们在处理一些数据的时候,需要用到柱状图,折线图和饼状图等来呈现数据,让用户能够对数据更加清晰明了化。下面我们来看一下简单的饼状图的实现。 延展 #import "nsobjec...

有时候我们在处理一些数据的时候,需要用到柱状图,折线图和饼状图等来呈现数据,让用户能够对数据更加清晰明了化。下面我们来看一下简单的饼状图的实现。

延展

#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 
    #import 

    @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 
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 *)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 
@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

效果图

iOS 饼状图的封装与实现
iOS 饼状图的封装与实现

饼状图效果图。本文的demo是借鉴自网上,非博主纯原创。敬请谅解。