效果图
在.h中只有一个方法,传入X轴、Y轴刻度值数组及数据数组即可方便的绘制图表。
@interface ZKChartView : UIView
/**
* 绘制图表
*
* @param xLabels X轴刻度值
* @param yLabels Y轴刻度值
* @param values 数据
*
*/
- (void)reloadWithXLabels:(NSArray *)xLabels
yLabels:(NSArray *)yLabels
values:(NSArray *)values;
@end
在UIViewController中调用
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 图表
ZKChartView *chartView=[[ZKChartView alloc]initWithFrame:CGRectMake(0, 20, 375, 234)];
chartView.backgroundColor = [UIColor colorWithRed:39/255.0 green:52/255.0 blue:68/255.0 alpha:1.0];
// 图表标题
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 20, 375, 44)];
titleLabel.textAlignment = NSTextAlignmentCenter;
titleLabel.textColor = [UIColor whiteColor];
titleLabel.font = [UIFont systemFontOfSize:16.0];
titleLabel.text = @"2018年12年第3周营业额";
[self.view addSubview:chartView];
[self.view addSubview:titleLabel];
// 图表数据
[chartView reloadWithXLabels:@[ @"12/16", @"12/17", @"12/18", @"12/19", @"12/20", @"12/22", @"12/23" ]
yLabels:@[ @"50000.00", @"40000.00", @"30000.00", @"20000.00", @"10000.00"]
values:@[ @"36000.00", @"28000.00", @"40000.00", @"42000.00", @"39000.00", @"44000.00", @"32000.00" ]];
}
@end
前言
APP开发中经常有一些绘制图表的需求,本文采用在UIView中通过addSublayer:方法添加各种CALayer来实现基本的绘图,用到以下CALayer
- CAGradientLayer 绘制图表渐变色
- CAShapeLayer 绘制线条
- CATextLayer 绘制文字
- CALayer 绘制圆点
以上CALayer的用法都比较简单,本文主要叙述绘制图表的基本思路,满足一般图表的绘制需求。
基本思路
- 确定在UIView中绘图的区域(红色区域),构建坐标系。
- 绘制X轴、Y轴刻度线,确定对应的刻度文字显示区域。
- 绘制各个点值对应的线,更新X轴Y轴刻度文字。
- 绘制图表渐变色。
- 给UIView添加手势,点击图表时显示对应的值。
代码实现
- 确定在UIView中绘图的区域,构建坐标系。
图表左侧为Y轴刻度值,底部为X轴刻度线及刻度值,顶部为了显示图表标题,因此在代码中用CGRect contentRect变量来设定图表据上下左右的距离,绘制图表时以此来确定各个值对应的位置。
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// 图表区域
UIEdgeInsets contentInsets = UIEdgeInsetsMake(48, 53, 34, 20);
self.contentRect = CGRectMake(contentInsets.left,
contentInsets.top,
CGRectGetWidth(frame) - contentInsets.left - contentInsets.right,
CGRectGetHeight(frame) - contentInsets.top - contentInsets.bottom);
// 初始化X轴坐标
[self initXLines];
[self initXLabels];
// 初始化Y轴坐标
[self initYLines];
[self initYLabels];
}
return self;
}
- 绘制X轴、Y轴刻度线,确定对应的刻度文字显示区域。
图表区域在Y轴方向上分为4等份,X轴上分为6等份。所以需要在图表区域画5条水平线,X轴上画7条垂直线。
- 绘制Y轴水平线,根据contentRect来计算5条线的起始坐标,用UIBezierPath绘制路径,CAShapeLayer画线,然后把5条线添加到CALayer上,再添加到UIView上。
// Y轴等分线
- (void)initYLines {
NSUInteger lineCount = 5; // 等分线数量
UIColor *lineColor = [UIColor colorWithRed:124/255.0 green:133/255.0 blue:138/255.0 alpha:1.0]; // 等分线颜色
CGFloat lineWidth = 0.5; // 等分线宽度
CGFloat lineSpace = CGRectGetHeight(self.contentRect) / (lineCount - 1);
self.yPoints = [NSMutableArray arrayWithCapacity:lineCount];
CGFloat startX = CGRectGetMinX(self.contentRect);
CGFloat endX = CGRectGetMaxX(self.contentRect);
CGFloat startY = CGRectGetMinY(self.contentRect);
CALayer *yLayer = [CALayer layer];
for (NSInteger i = 0; i < lineCount; i++) {
CGPoint startPoint = CGPointMake(startX, startY + lineSpace * i);
CGPoint endPoint = CGPointMake(endX, startPoint.y);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:startPoint];
[path addLineToPoint:endPoint];
CAShapeLayer *lineLayer = [CAShapeLayer layer];
lineLayer.fillColor = [UIColor clearColor].CGColor;
lineLayer.strokeColor = lineColor.CGColor;
lineLayer.lineWidth = lineWidth;
lineLayer.path = path.CGPath;
[yLayer addSublayer:lineLayer];
// 记录
[self.yPoints addObject:[NSValue valueWithCGPoint:startPoint]];
}
[self.layer addSublayer:yLayer];
}
- 绘制Y轴刻度线对应的刻度值,CATextLayer可以设定背景色、文字的颜色和字体等,用来绘制文字。刻度值文字的中心和水平线对齐,第1步中左侧预留的空间来放置刻度值文字。
// Y轴刻度值
- (void)initYLabels {
UIFont *font = [UIFont systemFontOfSize:8 weight:UIFontWeightLight]; // 文字字体
self.yLayers = [NSMutableArray arrayWithCapacity:self.yPoints.count];
for (NSUInteger i = 0; i < self.yPoints.count; i++) {
NSValue *value = self.yPoints[i];
CGPoint point = [value CGPointValue];
CATextLayer *textLayer = [self textLayerWithFont:font];
textLayer.frame = CGRectMake(0, point.y - 5, 49, 10); // frame设定是关键
[self.layer addSublayer:textLayer];
[self.yLayers addObject:textLayer];
}
}
- 绘制X轴刻度线、刻度值同理
- 绘制各个点值对应的线,更新X轴Y轴刻度文字。
- 绘制图表渐变色。
绘制点值线,主要是计算数据值对应的坐标点,用UIBezierPath绘制路径。用CAGradientLayer来绘制渐变色,数据对应的点值线增加起始点来作为CAGradientLayer的mask,这样就会在绘图区域形成渐变色,其他区域不显示渐变色。
/**
* 绘制图表
*
* @param xLabels X轴刻度值
* @param yLabels Y轴刻度值
* @param values 数据
*
*/
- (void)reloadWithXLabels:(NSArray *)xLabels yLabels:(NSArray *)yLabels values:(NSArray *)values {
// X轴刻度值
for (NSUInteger i = 0; i < xLabels.count; i++) {
CATextLayer *textLayer = self.xLayers[i];
textLayer.string = xLabels[i];
}
// Y轴刻度值
for (NSUInteger i = 0; i < yLabels.count; i++) {
CATextLayer *textLayer = self.yLayers[i];
textLayer.string = yLabels[i];
}
// Y坐标范围
NSValue *firstYPointValue = self.yPoints.firstObject;
NSValue *lastYPointValue = self.yPoints.lastObject;
CGFloat minY = [firstYPointValue CGPointValue].y;
CGFloat maxY = [lastYPointValue CGPointValue].y;
// 值范围
double minValue = [yLabels.lastObject doubleValue];
double maxValue = [yLabels.firstObject doubleValue];
self.valuePoints = [NSMutableArray arrayWithCapacity:values.count];
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
CGPoint startPoint = CGPointZero;
for (NSUInteger i = 0; i < values.count; i++) {
NSValue *xPointValue = self.xPoints[i];
CGFloat x = [xPointValue CGPointValue].x;
double value = [values[i] doubleValue];
CGFloat y = minY + (maxValue - value) / (maxValue - minValue) * (maxY - minY);
if (i == 0) {
startPoint = CGPointMake(x, y);
[bezierPath moveToPoint:startPoint];
} else {
CGPoint endPoint = CGPointMake(x, y);
CGPoint midPoint = [self midPointForStartPoint:startPoint endPoint:endPoint];
[bezierPath addQuadCurveToPoint:midPoint controlPoint:[self controlPointForStartPoint:midPoint endPoint:startPoint]];
[bezierPath addQuadCurveToPoint:endPoint controlPoint:[self controlPointForStartPoint:midPoint endPoint:endPoint]];
startPoint = endPoint;
}
[self.valuePoints addObject:[NSValue valueWithCGPoint:startPoint]];
}
self.contentLayer.path = bezierPath.CGPath;
// 渐变色
if (![self.layer.sublayers containsObject:self.gradientLayer]) {
[self.layer insertSublayer:_gradientLayer atIndex:0];
}
CGPoint lastPoint = [self.valuePoints.lastObject CGPointValue];
CGPoint firstPoint = [self.valuePoints.firstObject CGPointValue];
[bezierPath addLineToPoint:CGPointMake(lastPoint.x, maxY)];
[bezierPath addLineToPoint:CGPointMake(firstPoint.x, maxY)];
((CAShapeLayer *)self.gradientLayer.mask).path = bezierPath.CGPath;
}
- 给UIView添加手势,点击图表时显示对应的值。
点击UIView时,获取手势的坐标,从而判断点击的位置是否在图表区域,获取附近的数据值点。
- 新增加一个CALayer绘制一个圆点,凸显当前数据点。
- 另外新增加一个CATextLayer显示当前数据点的数据。
#pragma mark - 手势处理
- (void)tapGestureHandler:(UIGestureRecognizer *)gestureRecognizer {
CGPoint location = [gestureRecognizer locationInView:self];
if (CGRectContainsPoint(self.contentRect, location) ) {
CGFloat space = CGRectGetWidth(self.contentRect) / (self.xPoints.count - 1);
NSInteger index = roundf((location.x - CGRectGetMinX(self.contentRect)) / space);
// 选中圆点
NSValue *pointValue = self.valuePoints[index];
CGPoint point = [pointValue CGPointValue];
if (![self.layer.sublayers containsObject:self.dotLayer]) {
[self.layer addSublayer:self.dotLayer];
}
self.dotLayer.hidden = NO;
self.dotLayer.position = CGPointMake(point.x, point.y);
// 弹出文本
CGRect frame = CGRectMake(point.x, point.y, 60, 30);
CGFloat midX = CGRectGetMidX(self.contentRect);
CGFloat midY = CGRectGetMidY(self.contentRect);
if (point.x < midX) { // 弹出文本显示在图表水平中心右侧
frame.origin.x += 5;
} else { // 弹出文本显示在图表水平中心左侧
frame.origin.x -= (5 + CGRectGetWidth(frame));
}
if (point.y < midY) { // 弹出文本显示在图表垂直中心上侧
frame.origin.y += 5;
} else { // 弹出文本显示在图表垂直中心下侧
frame.origin.y -= (5 + CGRectGetHeight(frame));
}
if (![self.layer.sublayers containsObject:self.popupLayer]) {
[self.layer addSublayer:self.popupLayer];
}
self.popupLayer.hidden = NO;
self.popupLayer.string = [self attributedStringWithText:self.values[index]];
self.popupLayer.frame = frame;
}
}
后记
以上是绘制图表的基本思路,可基本满足绘制常见图表的需求。在实际项目中,还要根据具体的需求,比如绘制柱状图,K线图,弹出提示有多行等等,灵活运用CALayer来实现,万变不离其宗。
完整代码
//
// ZKChartView.m
// Layer
//
// Created by Evan on 2018/12/22.
// Copyright © 2018年 Evan. All rights reserved.
//
#import "ZKChartView.h"
@interface ZKChartView ()
@property (nonatomic, assign) CGRect contentRect; // 图表区域
@property (nonatomic, strong) CAGradientLayer *gradientLayer; // 渐变色
@property (nonatomic, strong) CAShapeLayer *maskLayer; // 渐变色
@property (nonatomic, strong) CAShapeLayer *contentLayer; // 数据线Layer
@property (nonatomic, strong) CALayer *dotLayer; // 点击图表时,代表当前值的圆点
@property (nonatomic, strong) CATextLayer *popupLayer; // 点击图表时,弹出的文本Layer
@property (nonatomic, strong) NSMutableArray *xLayers; // X轴刻度Layers
@property (nonatomic, strong) NSMutableArray *yLayers; // Y轴刻度Layers
@property (nonatomic, strong) NSMutableArray *xPoints; // X轴刻度对应的CGPoint
@property (nonatomic, strong) NSMutableArray *yPoints; // Y轴刻度对应的CGPoint
@property (nonatomic, strong) NSMutableArray *valuePoints; // 数据对应的CGPoint
@property (nonatomic, copy) NSArray *values; // 数据
@end
@implementation ZKChartView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// 图表区域
UIEdgeInsets contentInsets = UIEdgeInsetsMake(48, 53, 34, 20);
self.contentRect = CGRectMake(contentInsets.left,
contentInsets.top,
CGRectGetWidth(frame) - contentInsets.left - contentInsets.right,
CGRectGetHeight(frame) - contentInsets.top - contentInsets.bottom);
// 初始化X轴坐标
[self initXLines];
[self initXLabels];
// 初始化Y轴坐标
[self initYLines];
[self initYLabels];
}
return self;
}
/**
* 绘制图表
*
* @param xLabels X轴刻度值
* @param yLabels Y轴刻度值
* @param values 数据
*
*/
- (void)reloadWithXLabels:(NSArray *)xLabels yLabels:(NSArray *)yLabels values:(NSArray *)values {
// 禁用手势
self.userInteractionEnabled = NO;
// 数据校验
if (xLabels.count != self.xLayers.count ||
yLabels.count != self.yLayers.count ||
values.count != self.xPoints.count) {
return;
}
// X轴刻度值
for (NSUInteger i = 0; i < xLabels.count; i++) {
CATextLayer *textLayer = self.xLayers[i];
textLayer.string = xLabels[i];
}
// Y轴刻度值
for (NSUInteger i = 0; i < yLabels.count; i++) {
CATextLayer *textLayer = self.yLayers[i];
textLayer.string = yLabels[i];
}
// Y坐标范围
NSValue *firstYPointValue = self.yPoints.firstObject;
NSValue *lastYPointValue = self.yPoints.lastObject;
CGFloat minY = [firstYPointValue CGPointValue].y;
CGFloat maxY = [lastYPointValue CGPointValue].y;
// 值范围
double minValue = [yLabels.lastObject doubleValue];
double maxValue = [yLabels.firstObject doubleValue];
self.valuePoints = [NSMutableArray arrayWithCapacity:values.count];
UIBezierPath *bezierPath = [UIBezierPath bezierPath];
CGPoint startPoint = CGPointZero;
for (NSUInteger i = 0; i < values.count; i++) {
NSValue *xPointValue = self.xPoints[i];
CGFloat x = [xPointValue CGPointValue].x;
double value = [values[i] doubleValue];
CGFloat y = minY + (maxValue - value) / (maxValue - minValue) * (maxY - minY);
if (i == 0) {
startPoint = CGPointMake(x, y);
[bezierPath moveToPoint:startPoint];
} else {
CGPoint endPoint = CGPointMake(x, y);
CGPoint midPoint = [self midPointForStartPoint:startPoint endPoint:endPoint];
[bezierPath addQuadCurveToPoint:midPoint controlPoint:[self controlPointForStartPoint:midPoint endPoint:startPoint]];
[bezierPath addQuadCurveToPoint:endPoint controlPoint:[self controlPointForStartPoint:midPoint endPoint:endPoint]];
startPoint = endPoint;
}
[self.valuePoints addObject:[NSValue valueWithCGPoint:startPoint]];
}
self.contentLayer.path = bezierPath.CGPath;
// 渐变色
if (![self.layer.sublayers containsObject:self.gradientLayer]) {
[self.layer insertSublayer:_gradientLayer atIndex:0];
}
CGPoint lastPoint = [self.valuePoints.lastObject CGPointValue];
CGPoint firstPoint = [self.valuePoints.firstObject CGPointValue];
[bezierPath addLineToPoint:CGPointMake(lastPoint.x, maxY)];
[bezierPath addLineToPoint:CGPointMake(firstPoint.x, maxY)];
((CAShapeLayer *)self.gradientLayer.mask).path = bezierPath.CGPath;
// 启用手势
self.userInteractionEnabled = YES;
if (self.gestureRecognizers.count == 0) {
[self addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureHandler:)]];
}
self.values = [NSArray arrayWithArray:values];
}
#pragma mark - 手势处理
- (void)tapGestureHandler:(UIGestureRecognizer *)gestureRecognizer {
CGPoint location = [gestureRecognizer locationInView:self];
if (CGRectContainsPoint(self.contentRect, location) ) {
CGFloat space = CGRectGetWidth(self.contentRect) / (self.xPoints.count - 1);
NSInteger index = roundf((location.x - CGRectGetMinX(self.contentRect)) / space);
if (index >= self.values.count) { // 防止数组越界
return;
}
// 选中圆点
NSValue *pointValue = self.valuePoints[index];
CGPoint point = [pointValue CGPointValue];
if (![self.layer.sublayers containsObject:self.dotLayer]) {
[self.layer addSublayer:self.dotLayer];
}
self.dotLayer.hidden = NO;
self.dotLayer.position = CGPointMake(point.x, point.y);
// 弹出文本
CGRect frame = CGRectMake(point.x, point.y, 60, 30);
CGFloat midX = CGRectGetMidX(self.contentRect);
CGFloat midY = CGRectGetMidY(self.contentRect);
if (point.x < midX) { // 弹出文本显示在图表水平中心右侧
frame.origin.x += 5;
} else { // 弹出文本显示在图表水平中心左侧
frame.origin.x -= (5 + CGRectGetWidth(frame));
}
if (point.y < midY) { // 弹出文本显示在图表垂直中心上侧
frame.origin.y += 5;
} else { // 弹出文本显示在图表垂直中心下侧
frame.origin.y -= (5 + CGRectGetHeight(frame));
}
if (![self.layer.sublayers containsObject:self.popupLayer]) {
[self.layer addSublayer:self.popupLayer];
}
self.popupLayer.hidden = NO;
self.popupLayer.string = [self attributedStringWithText:self.values[index]];
self.popupLayer.frame = frame;
// 3秒后隐藏选中远点及弹出文本
[UIView cancelPreviousPerformRequestsWithTarget:self selector:@selector(autoHidePopup) object:nil];
[self performSelector:@selector(autoHidePopup) withObject:nil afterDelay:3];
}
}
// 3秒后隐藏选中远点及弹出文本
- (void)autoHidePopup {
self.dotLayer.hidden = YES;
self.popupLayer.hidden = YES;
}
#pragma mark - 坐标系
// X轴等分线
- (void)initXLines {
NSUInteger lineCount = 7; // 等分线数量
UIColor *lineColor = [UIColor colorWithRed:214/255.0 green:214/255.0 blue:214/255.0 alpha:1.0]; // 等分线颜色
CGFloat lineWidth = 1.5; // 等分线宽度
CGFloat lineHeight = 4; // 等分线高度
CGFloat padding = 4; // 等分线离图表内容底部的高度
CGFloat lineSpace = CGRectGetWidth(self.contentRect) / (lineCount - 1);
self.xPoints = [NSMutableArray arrayWithCapacity:lineCount];
CGFloat startX = CGRectGetMinX(self.contentRect);
CGFloat startY = CGRectGetMaxY(self.contentRect) + padding;
CGFloat endY = startY + lineHeight;
CAShapeLayer *xLayer = [CAShapeLayer layer];
for (NSInteger i = 0; i < lineCount; i++) {
CGPoint startPoint = CGPointMake(startX + lineSpace * i, startY);
CGPoint endPoint = CGPointMake(startPoint.x, endY);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:startPoint];
[path addLineToPoint:endPoint];
CAShapeLayer *lineLayer = [CAShapeLayer layer];
lineLayer.fillColor = [UIColor clearColor].CGColor;
lineLayer.strokeColor = lineColor.CGColor;
lineLayer.lineWidth = lineWidth;
lineLayer.path = path.CGPath;
lineLayer.lineJoin = kCALineJoinRound;
[xLayer addSublayer:lineLayer];
// 记录
[self.xPoints addObject:[NSValue valueWithCGPoint:startPoint]];
}
[self.layer addSublayer:xLayer];
}
// Y轴等分线
- (void)initYLines {
NSUInteger lineCount = 5; // 等分线数量
UIColor *lineColor = [UIColor colorWithRed:124/255.0 green:133/255.0 blue:138/255.0 alpha:1.0]; // 等分线颜色
CGFloat lineWidth = 0.5; // 等分线宽度
CGFloat lineSpace = CGRectGetHeight(self.contentRect) / (lineCount - 1);
self.yPoints = [NSMutableArray arrayWithCapacity:lineCount];
CGFloat startX = CGRectGetMinX(self.contentRect);
CGFloat endX = CGRectGetMaxX(self.contentRect);
CGFloat startY = CGRectGetMinY(self.contentRect);
CALayer *yLayer = [CALayer layer];
for (NSInteger i = 0; i < lineCount; i++) {
CGPoint startPoint = CGPointMake(startX, startY + lineSpace * i);
CGPoint endPoint = CGPointMake(endX, startPoint.y);
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:startPoint];
[path addLineToPoint:endPoint];
CAShapeLayer *lineLayer = [CAShapeLayer layer];
lineLayer.fillColor = [UIColor clearColor].CGColor;
lineLayer.strokeColor = lineColor.CGColor;
lineLayer.lineWidth = lineWidth;
lineLayer.path = path.CGPath;
[yLayer addSublayer:lineLayer];
// 记录
[self.yPoints addObject:[NSValue valueWithCGPoint:startPoint]];
}
[self.layer addSublayer:yLayer];
}
// X轴刻度值
- (void)initXLabels {
UIFont *font = [UIFont systemFontOfSize:8 weight:UIFontWeightLight]; // 文字字体
self.xLayers = [NSMutableArray arrayWithCapacity:self.xPoints.count];
for (NSUInteger i = 0; i < self.xPoints.count; i++) {
NSValue *value = self.xPoints[i];
CGPoint point = [value CGPointValue];
CATextLayer *textLayer = [self textLayerWithFont:font];
textLayer.frame = CGRectMake(point.x - 12, point.y + 10, 24, 10); // frame设定是关键
[self.layer addSublayer:textLayer];
[self.xLayers addObject:textLayer];
}
}
// Y轴刻度值
- (void)initYLabels {
UIFont *font = [UIFont systemFontOfSize:8 weight:UIFontWeightLight]; // 文字字体
self.yLayers = [NSMutableArray arrayWithCapacity:self.yPoints.count];
for (NSUInteger i = 0; i < self.yPoints.count; i++) {
NSValue *value = self.yPoints[i];
CGPoint point = [value CGPointValue];
CATextLayer *textLayer = [self textLayerWithFont:font];
textLayer.frame = CGRectMake(0, point.y - 5, 49, 10); // frame设定是关键
[self.layer addSublayer:textLayer];
[self.yLayers addObject:textLayer];
}
}
#pragma mark - Getter
- (CAGradientLayer *)gradientLayer {
if (!_gradientLayer) {
_gradientLayer = [CAGradientLayer layer];
_gradientLayer.frame = self.bounds;
_gradientLayer.colors = @[ (id)[[[UIColor redColor] colorWithAlphaComponent:0.25] CGColor],
(id)[[[UIColor grayColor] colorWithAlphaComponent:0.25] CGColor]];
_gradientLayer.mask = [CAShapeLayer layer];
}
return _gradientLayer;
}
- (CAShapeLayer *)contentLayer {
if (!_contentLayer) {
UIColor *lineColor = [UIColor colorWithRed:215/255.0 green:45/255.0 blue:43/255.0 alpha:1.0]; // 线颜色
CGFloat lineWidth = 1.5; // 线宽度
_contentLayer = [CAShapeLayer layer];
_contentLayer.fillColor = [UIColor clearColor].CGColor;
_contentLayer.strokeColor = lineColor.CGColor;
_contentLayer.lineWidth = lineWidth;
[self.layer addSublayer:_contentLayer];
}
return _contentLayer;
}
- (CATextLayer *)popupLayer {
if (!_popupLayer) {
UIFont *font = [UIFont systemFontOfSize:10 weight:UIFontWeightBold];
UIColor *backgroundColor = [UIColor colorWithRed:240/255.0 green:128/255.0 blue:128/255.0 alpha:1.0];
_popupLayer = [self textLayerWithFont:font];
_popupLayer.backgroundColor = backgroundColor.CGColor;
_popupLayer.cornerRadius = 2;
}
return _popupLayer;
}
- (CALayer *)dotLayer {
if (!_dotLayer) {
UIColor *backgroundColor = [UIColor colorWithRed:240/255.0 green:128/255.0 blue:128/255.0 alpha:1.0];
_dotLayer = [CALayer layer];
_dotLayer.backgroundColor = backgroundColor.CGColor;
_dotLayer.bounds = CGRectMake(0, 0, 8, 8);
_dotLayer.cornerRadius = 4;
}
return _dotLayer;
}
#pragma mark - 辅助方法
// textLayer
- (CATextLayer *)textLayerWithFont:(UIFont *)font {
CFStringRef fontName = (__bridge CFStringRef)font.fontName;
CGFontRef fontRef = CGFontCreateWithFontName(fontName);
CATextLayer *textLayer = [CATextLayer layer];
textLayer.font = fontRef;
textLayer.fontSize = font.pointSize;
textLayer.foregroundColor = [UIColor whiteColor].CGColor;
textLayer.wrapped = YES;
textLayer.truncationMode = kCATruncationEnd;
textLayer.alignmentMode = kCAAlignmentCenter;
textLayer.allowsFontSubpixelQuantization = YES;
textLayer.contentsScale = [UIScreen mainScreen].scale;
CGFontRelease(fontRef);
return textLayer;
}
// 计算两个点的中点
- (CGPoint)midPointForStartPoint:(CGPoint)p1 endPoint:(CGPoint)p2 {
return CGPointMake((p1.x + p2.x) / 2, (p1.y + p2.y) / 2);
}
// 计算两个点的控制点
- (CGPoint)controlPointForStartPoint:(CGPoint)p1 endPoint:(CGPoint)p2 {
CGPoint controlPoint = [self midPointForStartPoint:p1 endPoint:p2];
CGFloat diffY = fabs(p2.y - controlPoint.y);
if (p1.y < p2.y) {
controlPoint.y += diffY;
} else if (p1.y > p2.y) {
controlPoint.y -= diffY;
}
return controlPoint;
}
// 弹出文本样式
- (NSAttributedString *)attributedStringWithText:(NSString *)text {
NSDictionary *attributes = @{ NSFontAttributeName : [UIFont systemFontOfSize:10 weight:UIFontWeightBold],
NSForegroundColorAttributeName : [UIColor whiteColor],
NSBaselineOffsetAttributeName : @(-10) };
return [[NSAttributedString alloc] initWithString:text attributes:attributes];
}
@end
网友评论