項目中需要統計數據展現, 採用了餅圖形式展現.如下圖所示:
一、了解貝塞爾曲線相關概念
貝塞爾曲線相關概念:
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];
這個時候就可以測試看效果了.