iOS-貝塞爾曲線之自定義餅圖

2021-02-14 Cocoa開發者社區

項目中需要統計數據展現, 採用了餅圖形式展現.如下圖所示:

一、了解貝塞爾曲線相關概念

貝塞爾曲線相關概念:

UIBezierPath :畫貝塞爾曲線的path類

UIBezierPath定義 : 貝賽爾曲線的每一個頂點都有兩個控制點,用於控制在該頂點兩側的曲線的弧度。

曲線的定義有四個點:起始點、終止點(也稱錨點)以及兩個相互分離的中間點。

滑動兩個中間點,貝塞爾曲線的形狀會發生變化。

UIBezierPath:對象是CGPathRef數據類型的封裝,可以方便的讓我們畫出矩形 、 橢圓 或者 直線和曲線的組合形狀.

使用貝塞爾曲線的基本步驟:

(1)創建一個Bezier path對象。

(2)使用方法moveToPoint:去設置初始線段的起點。

(3)添加line或者curve去定義一個或者多個subpaths。

(4)改變UIBezierPath對象跟繪圖相關的屬性。

初始化方法:

 + (instancetype)bezierPath;

創建一個矩形:

 + (instancetype)bezierPathWithRect:(CGRect)rect;

創建圓形或者橢圓形:

 + (instancetype)bezierPathWithOvalInRect:(CGRect)rect;

 + (instancetype)bezierPathWithRoundedRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius; // rounds all corners with the same horizontal and vertical radius

 + (instancetype)bezierPathWithRoundedRect:(CGRect)rect byRoundingCorners:(UIRectCorner)corners cornerRadii:(CGSize)cornerRadii;

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

 + (instancetype)bezierPathWithCGPath:(CGPathRef)CGPath;

最基本的使用方法是:

 // 設置描繪的起點

 - (void)moveToPoint:(CGPoint)point;

 

 // 畫直線

 - (void)addLineToPoint:(CGPoint)point;

 

// 畫曲線

// a.繪製二次貝塞爾曲線   分別對應終點和一個控制點

 - (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint

 

// b.繪製三次貝塞爾曲線   分別對應終點和兩個控制點

 - (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2;

 

 //  畫圓弧

 - (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise

二:、自定義餅圖的核心邏輯

自定義餅圖:

針對上面的餅圖, 實現主要思路:

1.初始化畫布

2.bezierPath形成閉合的扇形路徑

3.自定義餅圖填充顏色

4.餅圖的引出點及指引線

5.畫引出直線

6.添加餅圖相對應提示文字

7.空心展示餅圖

8.露出方法,在所需控制器裡調用即可

1. 初始化畫布

+ (instancetype)initWithFrame:(CGRect)frame {

    

    ZLBezierPieView *bezierCurveView = [[NSBundle mainBundle] loadNibNamed:@"ZLBezierPieView" owner:self options:nil].lastObject;

    bezierCurveView.frame = frame;

    

    //背景視圖

    UIView *backView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];

    backView.backgroundColor = [UIColor clearColor];

    [bezierCurveView addSubview:backView];

    

    myFrame = frame;

    return bezierCurveView;

}

2. bezierPath形成閉合的扇形路徑

UIBezierPath *bezierPath = [UIBezierPath bezierPathWithArcCenter:point

                                                                  radius:radius

                                                              startAngle:startAngle                                                                 endAngle:endAngle

                                                               clockwise:YES];

[bezierPath addLineToPoint:point];

[bezierPath closePath];

3. 自定義餅圖填充顏色

// 可自定義餅圖填充顏色(根據自己需求添加)

NSArray *redArray = @[@"46",@"255",@"62",@"254",@"253",@"153",@"110", @"173",@"223",@"196"];

NSArray *greenArray = @[@"191",@"48",@"209",@"199",@"109",@"208",@"123", @"110",@"142",@"193"];

NSArray *blueArray = @[@"238",@"145",@"185",@"17",@"31",@"60",@"254", @"157",@"36",@"48"];

// 填充色

UIColor *customColor = [UIColor colorWithRed:[[redArray objectAtIndex:i] intValue]/255.0  green:[[greenArray objectAtIndex:i] intValue]/255.0 blue:[[blueArray objectAtIndex:i] intValue]/255.0 alpha:1];

        

// 渲染

CAShapeLayer *shapeLayer = [CAShapeLayer layer];

shapeLayer.lineWidth = 1;

// 填充色

shapeLayer.fillColor = customColor.CGColor;

shapeLayer.path = bezierPath.CGPath;

[self.layer addSublayer:shapeLayer];

4. 餅圖的引出點及指引線

// 餅圖引出點

CGFloat pieX = point.x + (radius)*cos(startAngle+(endAngle-startAngle)/2);

CGFloat pieY = point.y + (radius)*sin(startAngle+(endAngle-startAngle)/2);

        

// 指引線引出點

CGFloat X = point.x + (radius+20)*cos(startAngle+(endAngle-startAngle)/2);

CGFloat Y = point.y + (radius+20)*sin(startAngle+(endAngle-startAngle)/2);

CGFloat lineWidth = 80;

// 繪製小圓點

CAShapeLayer *circleLayer = [CAShapeLayer layer];

circleLayer.frame = CGRectMake(0, 0, 1, 1); // 指定frame,只是為了設置寬度和高度

circleLayer.position = CGPointMake(X, Y); // 設置居中顯示

circleLayer.fillColor = [UIColor clearColor].CGColor; // 設置填充顏色

circleLayer.lineWidth = 2.0;

circleLayer.strokeColor = customColor.CGColor;

// 使用UIBezierPath創建路徑

CGRect frame = CGRectMake(0, 0, 2, 2);

UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:frame];

// 設置CAShapeLayer與UIBezierPath關聯

circleLayer.path = circlePath.CGPath;

// 將CAShaperLayer放到某個層上顯示

[self.layer addSublayer:circleLayer];

5. 畫引出直線

// 畫第一段直線

CAShapeLayer *lineLayer = [CAShapeLayer layer];

lineLayer.frame = CGRectMake(0, 0, 1, 1); // 指定frame,只是為了設置寬度和高度

lineLayer.fillColor = [UIColor clearColor].CGColor; // 設置填充顏色

lineLayer.lineWidth = 1.0;

lineLayer.strokeColor = customColor.CGColor;

UIBezierPath *indicatrixLine = [UIBezierPath bezierPath];

[indicatrixLine moveToPoint:CGPointMake(pieX, pieY)];

[indicatrixLine addLineToPoint:CGPointMake(X, Y)];

lineLayer.path = indicatrixLine.CGPath;

lineLayer.lineWidth = 1.0;

lineLayer.strokeColor = customColor.CGColor;

[self.layer addSublayer:lineLayer];

if (X < point.x) { // 餅圖左側

    X = X - lineWidth;

}

// 添加指引線(第二段直線)

UIView *line = [[UIView alloc] initWithFrame:CGRectMake(X, Y, lineWidth, 1)];

line.backgroundColor = customColor;

[self.subviews[0] addSubview:line];

6. 添加餅圖相對應提示文字

// 添加文字

UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(X, Y - 15, lineWidth, 30)];

label.font = [UIFont systemFontOfSize:13];

label.textColor = customColor;

label.numberOfLines = 0;

label.text = type_names[i];

label.attributedText = [self setupAttriLabelWithTitleStr:type_names[i] ValueStr:[NSString stringWithFormat:@"%@", targetValues[i]]];

[self.subviews[0] addSubview:label];

if (X < point.x) { // 餅圖左側

    label.textAlignment = NSTextAlignmentLeft;

} else {

    label.textAlignment = NSTextAlignmentRight;

}

/**

 * label 的富文本布局

 *

 * titleStr 標題

 * ValueStr 值

 */

- (NSMutableAttributedString *)setupAttriLabelWithTitleStr:(NSString *)titleStr ValueStr:(NSString *)valueStr {

    

    NSMutableAttributedString *string = [[NSMutableAttributedString alloc]initWithString:[NSString stringWithFormat:@"%@\n%@", titleStr, valueStr]];

    [string addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:12] range:NSMakeRange(titleStr.length+1, valueStr.length)];    

    return string;

}

7. 空心展示餅圖

// 畫一個圓, 用來空心(如果滿圓則可以不要這塊)

UIBezierPath *radiusPath = [UIBezierPath bezierPathWithArcCenter:point radius:radius * 0.3 startAngle:0 endAngle:2*M_PI clockwise:YES];

[radiusPath addLineToPoint:point];

[radiusPath closePath];

CAShapeLayer *shapeLayer = [CAShapeLayer layer];

shapeLayer.lineWidth = 1;

shapeLayer.fillColor = [UIColor whiteColor].CGColor;

shapeLayer.path = radiusPath.CGPath;

[self.layer addSublayer:shapeLayer];

8. 露出方法,在所需控制器裡調用即可

/**

 *  畫餅狀圖

 *  @param type_names   分類名稱值

 *  @param targetValues 所有目標值

 */

- (void)drawPieChartViewWithType_Names:(NSMutableArray *)type_names TargetValues:(NSMutableArray *)targetValues;

初始化:

// 餅圖相關

@property (strong, nonatomic) ZLBezierPieView *pieChartView;

@property (strong, nonatomic) NSMutableArray *type_names; // 類型名稱

@property (strong, nonatomic) NSMutableArray *type_values; // 數據值

懶加載:

#pragma mark - 懶加載

// 餅圖類型名稱

- (NSMutableArray *)type_names {

    if (!_type_names) {

        _type_names = [NSMutableArray array];

        _type_names = [NSMutableArray arrayWithArray:@[@"主糧系列",@"零食世界",@"益智玩具",@"衣服狗窩",@"保健醫用",@"活體",@"日用系列"]];

    }

    return _type_names;

}

// 餅圖類型數據

- (NSMutableArray *)type_values {

    if (!_type_values) {

        _type_values = [NSMutableArray array];

        _type_values = [NSMutableArray arrayWithArray:@[@"100",@"100",@"100",@"200",@"200",@"100",@"200"]];

        

    }

    return _type_values;

}

餅圖畫布初始化:

// 餅圖畫布初始化

_pieChartView = [ZLBezierPieView initWithFrame:CGRectMake(0, 100, [UIScreen mainScreen].bounds.size.width, 250)];

_pieChartView.backgroundColor = [UIColor clearColor];

[self.view addSubview:_pieChartView];

// 餅圖

[_pieChartView drawPieChartViewWithType_Names:self.type_names TargetValues:self.type_values];

這個時候就可以測試看效果了.

相關焦點

  • 貝塞爾曲線之愛心點讚代碼全解析!| CSDN 博文精選
    本案例是由我自己寫的,因為之前對這個貝塞爾曲線有一點點了解,還有無意間看到了這個效果,覺得挺贊的,就順便寫了一下demo,並且學習了一些關於貝塞爾曲線的相關知識。首先,要看懂本案例的代碼,你需要具備 Android 自定義 View 的基本知識,並且你還有了解一些關於貝塞爾曲線的公式和算法。不過沒關係,我們並不需要對貝塞爾深刻了解,只要會基本的根據公式,套用代碼就好了。來看一下貝塞爾曲線的一些相關知識,我也是從大佬的博客中學習得來的。我們來看看什麼是貝塞爾曲線?
  • 十分鐘搞定酷炫動畫,Android自定義 View 入門
    好吧,懵逼歸懵逼,效果還是要做出來,作為一隻沒怎麼寫過動效的猿,第一反應就是讓 Ui 做成 gif 圖,然後 Ios 的哥們說 gif 圖內存大,容易失真,我們都已經用貝塞爾曲線做出來了(Ios 比我們 android
  • 程序丨曲線和樣條曲線,如何製作你自己的軌跡?
    譯者:陳敬鳳(nunu)審校:王磊(未來的未來)這篇教程將會講授從創建一條簡單的線段到編輯你自己貝塞爾樣條曲線的全部內容。 你在這篇文章中會學到以下這些東西:l  創建自定義編輯器。l  在場景視圖裡面進行繪製。l  創建貝塞爾樣條曲線並理解他們後面的數學含義。l  繪製曲線及其運動方向。
  • iOS開關按鈕,純CSS給你安排上了
    <input class="ios-switch" type="checkbox">從上述兩張截圖可抽象出iOS開關按鈕具有以下屬性的類,這個類可繼承到內部細節中。「細微變化」:灰色的背景區域快速縮小並顯示綠色的背景區域過渡動畫的貝塞爾曲線可用https://cubic-bezier.com微調到你想要的效果。為了不引入太多HTML標籤,iOS開關按鈕的背景使用偽元素::before代替。
  • 如何在 ios14 版本設置自定義充電提示音(賈維斯音效)
    設置賈維斯音效快捷指令1.1.打開 ios14 內置的 Shortcuts 原生應用1.2在檢索框檢索Decode(解碼), 選擇Base64 Encode(用Base64來解碼)添加解碼成功後, 如下所示, 點擊Encode位置將Encode更改為Decode, 解碼部分設置完成如下圖所示, 點擊藍色新建按鈕1.7.添加播放語音操作
  • Mac電腦怎麼錄屏,自定義創建錄製任務!
    多種模式供您選擇,自定義創建錄製任務易我錄屏助手Mac版為您提供錄製Mac屏幕或iOS設備所有需要的功能,簡單易操作,包括視頻錄製(錄製電腦屏幕、攝像頭、iOS設備)和音頻錄製等。能夠靈活地幫您錄製,您可以自定義錄製窗口的尺寸,還可以選擇錄製Mac系統聲音、麥克風聲音或兩者一起錄製。
  • iOS 14自定義桌面太美了 手把手教你重溫青春
    由於 iOS14更改APP圖標過於簡單,今天小編就帶來一期 iOS14自定義美化桌面教程,裡面會涉及到APP圖標、隱藏APP、桌面組件使用等部分,希望對大家會有所幫助。 下面以磚紅色壁紙+同色系圖標自定義 iOS 14 桌面為例。首先需要將 壁紙 和 圖標 保存到 iPhone 相冊。點擊下方所需要的常用APP圖標和壁紙保存到相冊即可。
  • 數學建模22:Voronoi圖與Dirichlet自由變形
    本講導讀當我們拍了照片要修圖的時候數學裡的Voronoi圖和基於它的Dirichlet自由變形方法可以解決這個問題——Voronoi圖是中學數學中常見的幾何現象,我們在初中時尺規作圖尋找三角形的外心,其實就是在做3個點的Voronoi圖,而Dirichlet自由變形方法需要用到高中的平面向量知識以求取不規則四邊形的面積。本講適合在講授或學習完高中數學的平面向量、直線和圓章節後,作為數學建模材料在日常教學中講授或學習。
  • 人類跌落夢境ios怎麼退款-ios退款步驟圖
    人類跌落夢境ios怎麼退款?遊戲首發價是12元,玩家可在5天內申請退款,下面爪遊控為大家帶來人類跌落夢境ios退款步驟圖。人類跌落夢境ios退款步驟圖  以上是小編為各位帶來人類跌落夢境ios退款步驟圖的全部內容,更多精彩遊戲資訊,請持續關注爪遊控
  • iOS14小組件照片怎麼設置固定圖片?照片小組件自定義方法[多圖]
    iOS14正式版中最受歡迎的應該就是小組件這個新功能,主要是可以讓用戶自定義桌面上的小組件功能,通過小組件就可以一眼看到想要的重要信息。其中照片的小組件是要怎麼固定圖片呢,我們來看下自定義照片小組件的操作方法。
  • EXCEL這麼漂亮的Excel餅圖,是怎麼做出來的?
    作者:蘭色幻想-趙志東 轉自:Excel精英培訓個人資產分配表如果想用餅圖展示,可以通過插入 - 餅圖 - 三維餅圖但蘭色從模板庫下載的餅圖,卻比直接插入的漂亮多了。
  • ios14桌面相冊如何自定義?ios14桌面相冊自定義的方法[多圖]
    ios14桌面相冊如何自定義?ios14桌面相冊自定義的方法 1. 安裝萬能小組件! 2. 照片組件顯示的是回憶相冊或者是精選照片 沒有的話充電一晚上自動生成,然後可以移除精選照片或者改變回憶相冊的封面就可以實現更換照片組件的照片了。
  • Principle小技巧分享之數字變化+環形加載
    應用場景:2、圓環進度條,比如表示頁面數據讀取過程或審核過程準備素材,畫2個圓形成一個圓環,準備2個直徑和圓環寬度相等的圓點。局限性:若2個動畫的速度(貝塞爾曲線)設置成非Linear(勻速),那麼看起來像是兩段動畫,要都設置成Linear才行。(因為2段的速度非Linear的話,中間銜接處的速度也不一樣,造成銜接處有卡頓感,這樣看起來顯然不是一段連貫的動畫了)先理解下原理——將上述視頻中的環形顏色進行統一,從而完成「環形1圈加載」。
  • 蘋果自定義鈴聲怎麼刪除?刪除iPhone自定義鈴聲的兩種方法
    今天,芝麻科技網就來詳細介紹下蘋果手機刪除自定義鈴聲的2種常用方法,一起來看看吧。刪除iPhone自定義鈴聲其實很簡單,最方便的還是通過電腦上的iTunes或愛思助手來刪除,具體操作步驟如下: 1、將iPhone通過usb數據線與電腦連接;2、打開iTunes或愛思助手PC工具(本文以愛思為例),軟體識別到設備連接之後,點擊左側的「鈴聲」,接下來在右側就可以看到之前導入的所有iPhone自定義鈴聲了,勾選需要刪除的鈴聲(支持多選
  • Umbreon適用於iOS中網頁的可自定義黑暗模式
    近期涉及Umbreon適用於iOS中網頁的可自定義黑暗模式內容備受矚目,很多讀者對此也很有興趣,現在給大家羅列關於Umbreon適用於iOS中網頁的可自定義黑暗模式最新消息。安裝Umbreon之後,您會在「設置」應用中找到一個廣泛的首選項窗格,您可以在其中根據自己的喜好配置調整項:在這裡,您可以:按需切換調整為網頁啟用暗模式將自定義CSS格式與黑暗模式CSS結合選擇其他應用以禁用以下功能配置CSS注入延遲在黑暗模式下切換灰色背景,而不是黑色啟用網頁圖像亮度調整
  • 無雙戰機ios攻略:蘋果新手玩法技巧[多圖]
    無雙戰機ios怎麼玩?新手玩家對於遊戲的一些玩法掌握不太熟悉,所以小編準備了一些簡單的攻略,幫助新手玩家快速的掌握遊戲的技巧,想要快速上手的玩家,通過下面小編為大家準備的攻略,就能了解遊戲的玩法。無雙戰機ios攻略無雙戰機手遊擁有隨機三選一的升級功能,讓戰機的攻擊方式在戰鬥中自由搭配、任意組合,可以駕駛不同型號的戰機進行戰鬥,消滅敵人收集道具提升自己的火力,挑戰強大的
  • 餅圖圓環圖嵌套組合圖
    今天介紹的餅圖圓環圖嵌套組合圖,可以幫你拓展思路。本教程內容較多,擔心記不全的話,可以分享到朋友圈給自己備份一份。(長按識別二維碼)效果展示:餅圖圓環圖嵌套組合圖,如下圖所示。這個圖表中,有以下幾個特點:組合圖由內層餅圖和外層圓環圖嵌套在一起圓環圖深藍色表示完成率所佔百分比實際數據源更新後,圖表動態自動更新
  • iOS 14 教程:如何自定義主屏幕應用圖標?
    iOS 14 發布後,自定義主屏幕圖標成為了一種流行的趨勢,一些用戶通過「快捷指令」將默認的應用圖標替換為自定義圖標,然後將應用本來的圖標隱藏在 iOS 14 的應用資源庫中。我們來看看如何為應用向主屏幕添加自定義圖標,這種方法實質上就是通過創建快捷指令打開應用,然後為此快捷指令添加自定義圖標,在開始之前我們需要明確這種方法的兩個局限性:· 點擊自定義的應用圖標,會先啟動「快捷指令」,然後通過「快捷指令」啟動應用;· 沒有通知標記