最近項目需求,做了一個餅狀圖,整體效果還可以接受,故拿出來和大家分享一下,并希望能得到大神的一些改進建議或者意見~~
先上效果圖:
all.gif
下面簡單梳理一下整體的流程:
第一步:新建一個繼承于UIView的文件。
此時我們需要這幾樣東西:
(1)數據源(區分比率)。
(2)顏色源(視覺信息)。
(3)標題源(文本信息)。
第二步:拿到數據源之后開始處理數據,算出各個數據的占比。
第三步:選定圓心,使用BezierPath來畫圓并填充顏色。
需要注意的事:
(1)開始的弧度為-π/2,也就是從圓的頂部開始。
(2)第n個圓弧開始的弧度為第n-1個圓弧結束的弧度
(3)圓弧的弧度取自數據源對應項的比率*2π
此時我們能得到這樣的效果:
餅狀圖
第四步:我們要做一些友好的展示效果——動畫
動畫是通過maskLayer的strokeEnd做改變來實現動畫效果的。
核心的一張圖:
maskLayer的構造
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];
至此流程的簡單梳理已經完畢了,難度并不大,就是比較費心思而已。
如有疏漏不足,還請大神們多多指教~
共同進步,么么噠~
最后附上完整的代碼地址:
完整代碼