最近项目需求,做了一个饼状图,整体效果还可以接受,故拿出来和大家分享一下,并希望能得到大神的一些改进建议或者意见~~
先上效果图:
下面简单梳理一下整体的流程:
第一步:新建一个继承于UIView的文件。
此时我们需要这几样东西:
(1)数据源(区分比率)。
(2)颜色源(视觉信息)。
(3)标题源(文本信息)。
第二步:拿到数据源之后开始处理数据,算出各个数据的占比。
第三步:选定圆心,使用BezierPath来画圆并填充颜色。
需要注意的事:
(1)开始的弧度为-π/2,也就是从圆的顶部开始。
(2)第n个圆弧开始的弧度为第n-1个圆弧结束的弧度
(3)圆弧的弧度取自数据源对应项的比率*2π
此时我们能得到这样的效果:
第四步:我们要做一些友好的展示效果——动画
动画是通过maskLayer的strokeEnd做改变来实现动画效果的。
核心的一张图:
maskLayer的path是这样构造的,实际是红色的弧线,但是lineWidth(宽度)为圆饼的半径,这样我们就可以覆盖整个饼状图了,并可以通过控制strokeEnd来动画展示饼状图了。
第五步:点击效果
有了动画之后的饼状图用户感觉会好很多了,如果再加上交互,用户可以点击的话,那样就更棒了。
点击之后的处理是最关键的,贴上代码:
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//不能点击则不做处理了
if (!self.canClick) {
return;
}
CGPoint touchPoint = [[touches anyObject] locationInView:self];
for (CustomShapeLayer *shapeLayer in pieShapeLayerArray) {
//如果只有一个模块,那么动画就要变化了,不是简单的偏移了
if (self.segmentDataArray.count == 1) {
shapeLayer.isOneSection = YES;
}
//判断选择区域
shapeLayer.clickOffset = [self preferGetUserSetValue:self.clickOffsetSpace withDefaultValue:15];
if (CGPathContainsPoint(shapeLayer.path, 0, touchPoint, YES)) {
//修改选中状态
if (shapeLayer.isSelected) {
shapeLayer.isSelected = NO;
}else{
shapeLayer.isSelected = YES;
}
NSInteger index = [pieShapeLayerArray indexOfObject:shapeLayer];
//执行block并开始右侧小圆点动画
[self dealClickCircleWithIndex:index];
} else {
shapeLayer.isSelected = NO;
}
}
}
点击时,我们根据isSelected做不同的处理,YES时,表示被选中,然后该模块的饼状图移动出来,移动的动画使用CABasicAnimation对path做处理;NO时,表示恢复原位,将该模块的饼状图复原。需要注意的是当你点击A块时,A被移动出来,此时点击B块,则B块移动出来的同时,A也要恢复回去。
关键处理的代码:
if (isSelected) {
//center 往外围移动一点 使用cosf跟sinf函数
newCenterPoint = CGPointMake(_centerPoint.x + cosf((_startAngle + _endAngle) / 2) * offset, _centerPoint.y + sinf((_startAngle + _endAngle) / 2) * offset);
}
//创建一个path
UIBezierPath *path = [UIBezierPath bezierPath];
//起始中心点改一下
[path moveToPoint:newCenterPoint];
[path addArcWithCenter:newCenterPoint radius:_radius startAngle:_startAngle endAngle:_endAngle clockwise:YES];
[path addArcWithCenter:newCenterPoint radius:_innerRadius startAngle:_endAngle endAngle:_startAngle clockwise:NO];
[path closePath];
self.path = path.CGPath;
//添加动画
CABasicAnimation *animation = [CABasicAnimation animation];
//keyPath内容是对象的哪个属性需要动画
animation.keyPath = @"path";
//所改变属性的结束时的值
animation.toValue = path;
//动画时长
animation.duration = 0.35;
//添加动画
[self addAnimation:animation forKey:nil];
至此流程的简单梳理已经完毕了,难度并不大,就是比较费心思而已。
如有疏漏不足,还请大神们多多指教~
共同进步,么么哒~
最后附上完整的代码地址:
完整代码