美文网首页三方管理实用工具iOS开发必修课
我与那该死的设计师的曲线之战(iOS-Charts)

我与那该死的设计师的曲线之战(iOS-Charts)

作者: CZF峰峰 | 来源:发表于2016-12-21 16:57 被阅读6282次

前言

先直接上图看设计师要求的图片吧

首页

看到这个首页效果的时候觉得微微有点蛋疼,但觉得应该是可以的,没多想
后来才知道是我太年轻了。。

找了各种第三方,留给自己时间不多,基本没时间自己做一个出来,所以只能借助第三方,最后决定用 Charts,毕竟是大神而且和安卓一起开发遇到问题也可以一起讨论,start 也破万。

正文

不说废话了,直接开始吧,首先由于项目决定使用的语言是 OC 语言,charts 是 swift 语言,所以只能混编,项目最低支持版本8.0

关于charts 怎么集成到 OC 代码里面,我看过简书已经写了很多了,我就不重复了,而且也没什么技术难点,无非就是拖进去就好。我没有使用 pods 毕竟需要修改源码。。想到就略蛋疼。。。

基础集成

把 charts 集成好之后就可以使用了,先编译一遍确认能够正常编译通过,通过后就可以开始使用了。先说下基本的设置吧。我是用的曲线,也就是折线的分类,charts 有很多个模块,其他模块我没有去研究,毕竟没那么多时间,如果你用storyboard 创建的,那就是新建一个 view,类的名字直接为LineChartView,
如果是代码创建,也是直接用这个类创建一个 view 然后设置 frame 添加到父类 view 上就好了。

// chartview 本身的属性设置
self.chartView.delegate = self;// 代理
    self.chartView.backgroundColor = [UIColor clearColor];//设置背景颜色
    self.chartView.descriptionText = @"";//隐藏描述文字
    self.chartView.noDataText = @"暂时没有体重数据";// 设置没有数据的显示内容
    self.chartView.legend.enabled = NO;//不显示图例说明
    self.chartView.scaleYEnabled = NO;//取消Y轴缩放
    self.chartView.scaleXEnabled = NO;//取消X轴缩放
    self.chartView.scaleEnabled = NO;// 缩放
    self.chartView.doubleTapToZoomEnabled = NO;//取消双击缩放
    self.chartView.dragDecelerationEnabled = NO;//拖拽后是否有惯性效果
    self.chartView.dragDecelerationFrictionCoef = 0;//拖拽后惯性效果的摩擦系数(0~1),数值越小,惯性越不明显
    self.chartView.rightAxis.enabled = NO;//不绘制右边轴的信息
    self.chartView.leftAxis.enabled = NO;//不绘制左边轴的信息
    self.chartView.xAxis.enabled = NO;
    self.chartView.xAxis.drawGridLinesEnabled = NO;//不绘制网络线
    self.chartView.xAxis.axisLineColor = [UIColor whiteColor];// x 轴的网络线颜色
    self.chartView.xAxis.labelPosition = XAxisLabelPositionTopInside;// X轴的位置
    self.chartView.xAxis.granularity = 1;// 间隔为1

基本上看得懂英文的都懂得这些的意思
看效果图,需要点击的时候出现一个 view,跟随点的位置,charts 本身已经实现了这个功能,名字叫BalloonMarker,但是集成的时候没有集成过来,需要自己写,或者直接在 demo 里面搜索这个类拷贝到自己的项目中去修改

// 显示气泡效果
    BalloonMarker *marker = [[BalloonMarker alloc]
                             initWithColor: [UIColor colorWithRed:0.384 green:0.800 blue:0.980 alpha:1.000]
                             font: [UIFont systemFontOfSize:12.0]
                             textColor: UIColor.whiteColor
                             insets: UIEdgeInsetsMake(8.0, 8.0, 20.0, 8.0)];
    marker.img = [UIImage imageNamed:@"marker"];
    marker.chartView = self.chartView;
    self.chartView.marker = marker;

基础设置都设置好了,接下来就可以填充数据了,charts 你大致是可以理解为这样的工作模式:

设置 chartview 的属性 -> 设置LineChartData数据 -> 最后设置各种数据显示属性

这是我第一个坑,有一些属性是需要等后面数据填充了才可以进行设置,否者会遇到各种奇怪的问题。
我们继续进行数据的填充吧,chartview 可以设置多条线,每一条线就是一个LineChartDataSet,每一条线上面的点就是ChartDataEntry,想在一条线上显示多少个点就需要多少个ChartDataEntry。

NSMutableArray *arr = [NSMutableArray array];
for (int i = 0; i < self.weights.count; i++) {
    ChartDataEntry *entry = [[ChartDataEntry alloc] initWithX:i y:ww];
    entry.flag = model.weightflag;//自己增加的属性
    entry.realValue = model.weight;//也是自己增加的属性
    [arr addObject:entry];
}
LineChartDataSet *set = [[LineChartDataSet alloc] initWithValues:arr label:@""];
//对于线的各种设置
    set.drawValuesEnabled = NO;//不显示文字
    set.highlightEnabled = YES;//选中拐点,是否开启高亮效果(显示十字线)
    set.highlightColor = [UIColor clearColor];// 十字线颜色
    set.drawCirclesEnabled = YES;//是否绘制拐点
    set.cubicIntensity = 0.2;// 曲线弧度
    set.circleRadius = 5.0f;//拐点半径
    set.drawCircleHoleEnabled = NO;//是否绘制中间的空心
    set.circleHoleRadius = 4.0f;//空心的半径
    set.circleHoleColor = [UIColor whiteColor];//空心的颜色
    set.circleColors = @[[UIColor colorWithRed:0.114 green:0.812 blue:1.000 alpha:1.000]];
    set.mode = LineChartModeCubicBezier;// 模式为曲线模式
    set.drawFilledEnabled = YES;//是否填充颜色
// 设置渐变效果
[set setColor:[UIColor colorWithRed:0.114 green:0.812 blue:1.000 alpha:1.000]];//折线颜色
    NSArray *gradientColors = @[(id)[ChartColorTemplates colorFromString:@"#FFFFFFFF"].CGColor,
                                (id)[ChartColorTemplates colorFromString:@"#C4F3FF"].CGColor];
    CGGradientRef gradientRef = CGGradientCreateWithColors(nil, (CFArrayRef)gradientColors, nil);
    set.fillAlpha = 1.0f;//透明度
    set.fill = [ChartFill fillWithLinearGradient:gradientRef angle:90.0f];//赋值填充颜色对象
    CGGradientRelease(gradientRef);//释放gradientRef
// 把线放到LineChartData里面,因为只有一条线,所以集合里面放一个就好了,多条线就需要不同的 set 啦
LineChartData *data = [[LineChartData alloc] initWithDataSets:@[set]];

把线都设置好了,放在 data 里面后,还需要把 data 放在 chartview 的 data 里面,这样才算是填充了数据

self.chartView.data = [self setData]; // 加载数据

之后就进行最后的设置了

//设置最大显示值
NSMutableArray *arr = [NSMutableArray array];
for (NSUInteger i = 0; i < self.weights.count; i++) {
        FFWeightModel *model = self.weights[i];
        [arr addObject:@(model.weight)];
    }
double max = [[arr valueForKeyPath:@"@max.doubleValue"] doubleValue];
self.chartView.leftAxis.axisMaxValue = max;
//设置最小显示值
double min = [[arr valueForKeyPath:@"@min.doubleValue"] doubleValue];
self.chartView.leftAxis.axisMinValue = min;
// 设置区域显示,要显示7个数据,所以设置最大显示和最小显示
[_chartView setVisibleXRangeMaximum:6];// 最大显示
[_chartView setVisibleXRangeMinimum:6];// 最小显示

//设计师说要数据加载的时候前面留空白3个,拖到后面也是留空白3个,保证点都是在中间为准,所以又设置了留白数据
self.chartView.xAxis.axisMinimum = -3;//最前面留空白3个区域
self.chartView.xAxis.axisMaximum = self.weights.count+2.1;//后面留空3个区域,别问我为什么是2.1

//添加添加限制的线
ChartLimitLine *limitLine = [[ChartLimitLine alloc] initWithLimit:[self.userweight[@"weight_first"] doubleValue] label:[NSString stringWithFormat:@"初始%@kg",self.userweight[@"weight_first"]]];
    limitLine.lineWidth = 1;
    limitLine.lineDashLengths = @[@(5.0),@(5.0)];
    limitLine.lineColor = [UIColor colorWithRed:1.000 green:0.671 blue:0.671 alpha:1.000];
    limitLine.labelPosition = ChartLimitLabelPositionCenter;//位置
    limitLine.valueTextColor = [UIColor colorWithWhite:0.502 alpha:1.000];//label文字颜色
    limitLine.valueFont = [UIFont systemFontOfSize:12];//label字体
    
    ChartLimitLine *limitLine2 = [[ChartLimitLine alloc] initWithLimit:[self.userweight[@"weight_target"] doubleValue] label:[NSString stringWithFormat:@"目标%@kg",self.userweight[@"weight_target"]]];
    limitLine2.lineWidth = 1;
    limitLine2.lineDashLengths = @[@(5.0),@(5.0)];
    limitLine2.lineColor = [UIColor colorWithRed:0.682 green:0.925 blue:1.000 alpha:1.000];
    limitLine2.labelPosition = ChartLimitLabelPositionCenter;//位置
    limitLine2.valueTextColor = [UIColor colorWithWhite:0.502 alpha:1.000];//label文字颜色
    limitLine2.valueFont = [UIFont systemFontOfSize:12];//label字体

//把限制线添加到 chartview 里面
[self.chartView.leftAxis addLimitLine:limitLine];
[self.chartView.leftAxis addLimitLine:limitLine2];

//最后每次打开都自动高亮最后一个数据,所以需要设置
FFWeightModel *model = self.weights.lastObject;
[self.chartView highlightValueWithX:self.weights.count-1 y:model.realWeight dataSetIndex:0 callDelegate:NO];
//由于每次打开都是显示在最前面的点,所以需要移动显示的位置
[_chartView moveViewToX:self.weights.count];// 移动到那个点
//最后显示出动画
[self.chartView animateWithYAxisDuration:1];//动画

其实以上都是简单的设置,只要花时间就能设置出来,但是难点永远不是这些,是那些坑爹的需要,是那些不知道技术实现起来很难以为只是很简单的,所以需求总是天天有,只能拼命改改改T_T

说一些 charts 的一些坑吧

集成BalloonMarker的坑

我是直接拿 demo 的过来用的,如果不熟悉 swift 的同学就要注意了,你把 swift 的文件集成过来后,需要编译一次,然后你是不是想说应该怎么声明这个BalloonMarker,因为你在 import 里面没有看到这个,混编的 swift 其实全部都在一个不可见的 h 文件里面,你只需要 #import "你项目的名字-swift.h"这个文件就可以了,所有混编的 swift 都会在这里声明,不过你需要先编译一次才会在里面可以找到
对于BalloonMarker修改 view 的背景,他是直接画出来的,但设计师的那个是切图出来,所以你需要在BalloonMarker里面找到open override func draw(context: CGContext, point: CGPoint)这个方法,然后删除或者注释掉那个很长的属性if let color = color{},新建了一个属性叫open var img: UIImage?然后直接在被你删掉或者注释掉的地方添加

if let img = img{
            img.draw(at: CGPoint(x:rect.origin.x-2,y:rect.origin.y))
        }

编译一次后你就可以直接在外部调用 img 这个属性并且赋值了

数据源的坑

如果你需要显示中文,恭喜来这个坑,目前我没找到什么好的解决办法
比如想显示 x 轴的文字,就需要self.chartView.xAxis.valueFormatter = self;并且实现它的数据源代理就可以用他的方法了,- (NSString * _Nonnull)stringForValue:(double)value axis:(ChartAxisBase * _Nullable)axis;目前没有很好的利用这个方法,觉得不好用

代理的坑

charts 主要用到有三个代理

@objc optional func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight)
    
    // Called when nothing has been selected or an "un-select" has been made.
    @objc optional func chartValueNothingSelected(_ chartView: ChartViewBase)
    
    // Callbacks when the chart is moved / translated via drag gesture.
    @objc optional func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat)

第一个是点击后响应的方法,第二个是点击同一个点响应的方法,第三个是拖动就会响应的方法

先说第一个吧,点击后根据你点击的 x 的位置定位到你点击的那个点,并且高亮显示,如果你有用 markerview 就会显示出来,包括高亮线,由于我用不到,所以就设置高亮线为透明

我这边的要求是,不管点击哪个点,都要自动移动到中间,所以需要取出你点击的点,然后移动到中间
[self.chartView moveViewToAnimatedWithXValue:entry.x-3 yValue:0 axis:AxisDependencyLeft duration:.3];

相对来说这个是很容易实现的,毕竟你点击后能够得到对于的ChartDataEntry的值,所以算出你点击的位置然后设置在中间都是很容易的,但是滑动就难了,滑动的时候需要保证每次滑动都是在中间,随便说下,滑动方法里面的 dx 和 dy 是在你每次手指滑动的时候重新开始计算的,拿 dx 来说,你滑动左边数值是从0开始加上去的,右边滑动是从0到负数的叠加,还好 chartview 有两个属性可以用来做判断,分别是
self.chartView.highestVisibleX
self.chartView.lowestVisibleX
NSInteger index = self.chartView.highestVisibleX-3;拿到 index
然后显示高亮状态[self.chartView highlightValueWithX:index y:model.realWeight dataSetIndex:0 callDelegate:NO];
但是这样还是不行,毕竟滑动不会自动居中,所以就导致显示的点可能会不在中间,所以自己改源码增加了一个滑动结束的代理

高亮的坑

安卓那边高亮显示可以直接用
- (void)highlightValueWithX:(double)x dataSetIndex:(NSInteger)dataSetIndex;
但是 iOS 这边直接用这个方法是不行的,在 github 上也有人问过此类问题,最后只能用另一个属性方法
- (void)highlightValueWithX:(double)x y:(double)y dataSetIndex:(NSInteger)dataSetIndex;
但由于我在滑动代理里面增加了高亮的方法,高亮方法默认是打开代理的状态,这样会导致滑动出错,所以需要用另一个高亮显示的方法
- (void)highlightValueWithX:(double)x y:(double)y dataSetIndex:(NSInteger)dataSetIndex callDelegate:(BOOL)callDelegate;
设置代理为 NO 就可以了

总结

其实到了最后跟设计图还是有很多不同的,放出实际图,其实多少辛酸泪不是简单两语能够说得清道得明,排版有点乱,大家请将就下哈~这是第一版,希望以后再努力的去改一改吧!

打开 APP 后显示的效果 拖动到中间的效果

相关文章

  • 我与那该死的设计师的曲线之战(iOS-Charts)

    前言 先直接上图看设计师要求的图片吧 看到这个首页效果的时候觉得微微有点蛋疼,但觉得应该是可以的,没多想后来才知道...

  • 我那该死的婆婆

    早上从楼上来,小区里一对婆媳正拌着嘴,互不相让没一个省油的灯。看热闹的是看热闹,劝架的是劝架,总算是把两个炸了毛的...

  • 我那该死的班长

    01. 残阳如血,染红了整个天地,到处弥漫着刺鼻的硝烟的味道。阵地前,横七竖八的趴着、躺着的那些狗杂种,半个钟头前...

  • Charts:一个强大的iOS图表框架

    原文地址:http://hao.jobbole.com/ios-charts/ ios-charts是一个强大的图...

  • ios-charts:一个强大的iOS图表框架

    原文地址:http://hao.jobbole.com/ios-charts/ ios-charts是一个强大的图...

  • 我那该死的摩羯座老公!!

    呵,最近真的是对我的摩羯座老公忍无可忍。 原因是这样的,目前我怀孕36周+,两人异地。从不主动和我联系,即使有事...

  • 那该死的勾勾

    儿子上三年级时,我开始了单一的陪读生活,和所有妈妈一样,每天给孩子做着一日三餐,打扫房间,清洗衣物……不过,我给自...

  • 那该死的梦想

    前几日在车上,无意间听到一段广播,打进电话的人情绪及其低落,抱怨因为当年家里条件不好,他为了减轻家庭压力,被迫放弃...

  • 那该死的惰性

    我就是懒惰啊 明知道懒惰,可我就是提不起来劲,考试近在眼前,而我也像热锅上的蚂蚁一样,焦急难耐,可是书还是看不下去...

  • 那该死的拖延

    睁开眼睛的那一瞬间,便是每天的惊喜所在。 总以为明天会是今天的延续,怎奈它们毫无关系,只是记忆仍然存在。

网友评论

  • 1f258cdae6c2:滑动结束的代理 如何弄的?麻烦博主讲一下,最近项目遇到了
  • Heaven7th:请教一个问题,望楼主一定回复,如何获取点击每一个点的frame呢?第一个代理里面都是xy轴的值,求回复
  • 真爱要有你才完美:大神 如果想再x点和Y点间做个线 怎么做啊 就是 比如 X = 1的时候 Y = 2 那个点 和 X周上 1那个点 之间做个连线
  • 54dabbe03c92:老哥,我现在同时显示1000个点,发现cpu利用率99%了,而且还老是要刷新,导致手机都死机了,要怎么办呢
    54dabbe03c92:@safri 只能采样了。。我现在最多显示100个点,一班就几十个点
    e2ef036d1b32:你最后咋解决的啊
  • 54dabbe03c92:self.chartView.xAxis.valueFormatter = self会造成循环引用,还是另起一个类吧,不要用self
  • 问夕阙:self.chartView.descriptionText = @"";//隐藏描述文字 写这句话 会报错 是什么原因
    54dabbe03c92:或者直接禁止 lineChartView.chartDescription.enabled = NO;
    54dabbe03c92:@真坑_1cf3 lineChartView.chartDescription.text = @"";或者这样
    54dabbe03c92:ChartDescription *description = [[ChartDescription alloc] init];
    description.text = @"点的集合";
    description.textColor = [UIColor whiteColor];
    description.textAlign = NSTextAlignmentRight;
    lineChartView.chartDescription = description;
    换成这样
  • 真爱要有你才完美:朋友这个 我这有个需求是 我一个月30天把 我进来只让显示前五天 然后可以向左滑 看接下来的 天数的折线 这种怎么做啊
    54dabbe03c92:@真爱要有你才完美 在设置完data之后再设置范围,每次刷新完数据在设置一次
    真爱要有你才完美:@温水煮青蛙a 不行 啊 能加下QQ 我给你发个设计图 你看看怎么弄好么 :joy: 2428485037
    温水煮青蛙a:[_lineView setVisibleXRangeMaximum:10];// 最大显示
    [_lineView setVisibleXRangeMinimum:4];// 最小显示
    [_lineView moveViewToX:19];// 移动到那个点 x轴坐标
  • 温水煮青蛙a:楼主 我有个需求请教一下你 限制线 你这里设置的是水平方向的 我现在我要在竖直方向添加分割线 -> 比如我x轴有100条数据 每10条数据设置一条分割线 要怎么实现呢 真是搞不出来了 有时间的话 指点一下
  • Monkey_hbh:我想请问一下怎么通过滑动曲线获取中心index 对应数据源的索引是多少吗?
  • 真爱要有你才完美:朋友 能加个QQ 2428485037 问问你怎么做这种曲线的 折线图么
  • 94e75a86153b:请问这个气泡是怎么设置的呢?我按照你的方法设置了,没起作用,是不是气泡图片不对呢?
  • einsphy2017:楼主,我问个问题,我们现在要实现一个图表,有两条线,一条是已知的,静态的,页面载入就绘制,这个我已经实现了,还有一条是实时传过来的,需要一定时间绘制一段,直到时间结束,这个我是在一个通知里完成的,但是画不出来,望指教
  • 3b73ac1e6515:楼主请问BalloonMarker上面的kg怎么设置的
    CZF峰峰:@cd_chen 自定义 label 加上去~
  • wutengwei:请问点击一个显示高亮点击另一个前一个恢复怎么做?有QQ吗
    wutengwei:@CZF峰峰 好的,谢啦
    CZF峰峰:有个高亮代理方法,其实说实话,很多东西都需要自定义或者改源代码
  • 花漾爱意:怎样在某个点上,放一个图片啊?
    CZF峰峰:@花漾爱意 没试过放图片= =
  • Lin55:你好 刷新数据之后,ChartView保留了之前的contentsize大小 导致x轴数据点之间的间距特别大 有解决办法么
  • wutengwei:请问你是怎么实现的滑动呢??我现在有个需求是要分页
    真爱要有你才完美:@Lin55 朋友这个 我这有个需求是 我一个月30天把 我进来只让显示前五天 然后可以向左滑 看接下来的 天数的折线 这种怎么做啊
    Lin55:setVisibleXRangeMaximum
  • asdfeng:楼主,如果图表背景色不是白色的,该怎么绘制拐点的空心圆?我这里现在设置空心圆的背景色为clear后,显示的是曲线从空心圆里穿过了
    CZF峰峰:不能设置为透明,会穿透的,我之前是修改源代码,如果你背景颜色只有一种就设置那种背景颜色,多重背景颜色貌似没有什么好的办法
  • 0206b277d36d: 楼主好 我想请问下 怎么改变marker显示的内容呢 不是换图片,点击一个数据点他默认即是显示Y轴的值, 你这样后面带了一个 kg单位怎么设置呢 ,我试了半天没找到方法
    0206b277d36d:@CZF峰峰 改好了 ,改了一点点就行了, 只增加了一个公开属性,哈哈真开心。
    0206b277d36d:@CZF峰峰 是把那个label 公开出来了吗? 能不能教教我 我不会swift 不知道咋改balloonMarker那个类:cry:
    CZF峰峰:没方法,我是自己改源代码
  • 浮云千载唯忆君颜:谢谢博主分享,我这边有个问题求教~
    在marker出来的时候,比如在点(0, 0)显示着,体重40kg,这时候改变了数据源,这个点的数据变成50kg,但是marker的位置没变,就会在这个点的下面出现,不知道博主知不知道如何刷新marker。。。自己找半天找不到。。
    析臣:@浮云千载唯忆君颜 我修改的marker源码:smile:
    浮云千载唯忆君颜:@CZF峰峰 就给charts设置个marker,就能点出来了呀🤣我啥也没干。现在找不到这个点击方法,所以我不知道如何让他消失再出现,或者改变位置……
    CZF峰峰:你是怎么让marker 出来的就怎么刷新,刷新就是重新选择你的位置
  • 析臣:我想再问一下,就是数据量特别大的时候怎么设置X轴的显示数量
    析臣:@Coder_LiSir 谢谢,已经找到了,是labelCount这个属性,但是要配合forceLabelsEnabled一起使用才会有效果。
    0206b277d36d:xais.labelCount 有这个属性 , 间距是guaritary这个属性,英文单词可能拼写有错误差不多这样
    CZF峰峰:@析臣 我记得文章里面有说到设置显示数量的,具体你看下代码, 不管是设置 y 还是 x 都是一样的道理
  • 析臣:这句话拯救了我:self.chartView.xAxis.valueFormatter = self。感谢
  • 大码哥:兄弟,你是如何设置线最后一个点样式的。
    大码哥:@CZF峰峰 多谢
    CZF峰峰:改源码
  • 千机玄月:请问有遇到这个[_chartView setVisibleXRangeMaximum:6];
    [_chartView setVisibleXRangeMinimum:6]会不正确显示的问题么...我的数据不能按照设置显示,同样的数据设置第二次才对....
    CZF峰峰:你先加载数据self.chartView.data = [self setData];
    然后再设置显示数量什么的
  • Just_go:你那个marker为什么可以改背景色啊, 求指教
    CZF峰峰:可以的,你自己改源文件就好
  • 6a6db38116bc:请教一下BalloonMarker我在文件里搜索一直找不到,我是pod集成的
    CZF峰峰:@不请自来的蚊子 不客气哈~
    6a6db38116bc:@CZF峰峰 谢谢,我已经实现了,感谢回复。
    CZF峰峰:@不请自来的蚊子 那个是 demo 带的,你需要下载 github 的 demo 去搜索
  • lsif的简书:请问楼主,引入charts后,archive包怎加了二三十M,怎么解决?
    CZF峰峰:@OC_Demo 会变大的,你可以上传到 iTunesconnect 后查看在 AppStore 的大小
    lsif的简书:@CZF峰峰 是的呀,有影响?
    CZF峰峰:你是不是勾选了 bitcode

本文标题:我与那该死的设计师的曲线之战(iOS-Charts)

本文链接:https://www.haomeiwen.com/subject/iyrrmttx.html