聽說你想做個餅狀圖(iOS)

最近項目需求,做了一個餅狀圖,整體效果還可以接受,故拿出來和大家分享一下,并希望能得到大神的一些改進建議或者意見~~

先上效果圖:

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];

至此流程的簡單梳理已經完畢了,難度并不大,就是比較費心思而已。

如有疏漏不足,還請大神們多多指教~

共同進步,么么噠~

最后附上完整的代碼地址:
完整代碼

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容