前言
先直接上圖看設(shè)計(jì)師要求的圖片吧
看到這個(gè)首頁(yè)效果的時(shí)候覺(jué)得微微有點(diǎn)蛋疼斗锭,但覺(jué)得應(yīng)該是可以的由桌,沒(méi)多想
后來(lái)才知道是我太年輕了忍级。帆谍。
找了各種第三方,留給自己時(shí)間不多轴咱,基本沒(méi)時(shí)間自己做一個(gè)出來(lái)汛蝙,所以只能借助第三方烈涮,最后決定用 Charts,畢竟是大神而且和安卓一起開(kāi)發(fā)遇到問(wèn)題也可以一起討論窖剑,start 也破萬(wàn)跃脊。
正文
不說(shuō)廢話了,直接開(kāi)始吧苛吱,首先由于項(xiàng)目決定使用的語(yǔ)言是 OC 語(yǔ)言酪术,charts 是 swift 語(yǔ)言,所以只能混編翠储,項(xiàng)目最低支持版本8.0
關(guān)于charts 怎么集成到 OC 代碼里面绘雁,我看過(guò)簡(jiǎn)書(shū)已經(jīng)寫(xiě)了很多了,我就不重復(fù)了援所,而且也沒(méi)什么技術(shù)難點(diǎn)庐舟,無(wú)非就是拖進(jìn)去就好。我沒(méi)有使用 pods 畢竟需要修改源碼住拭。挪略。想到就略蛋疼。滔岳。杠娱。
基礎(chǔ)集成
把 charts 集成好之后就可以使用了,先編譯一遍確認(rèn)能夠正常編譯通過(guò)谱煤,通過(guò)后就可以開(kāi)始使用了摊求。先說(shuō)下基本的設(shè)置吧。我是用的曲線刘离,也就是折線的分類室叉,charts 有很多個(gè)模塊,其他模塊我沒(méi)有去研究硫惕,畢竟沒(méi)那么多時(shí)間茧痕,如果你用storyboard 創(chuàng)建的,那就是新建一個(gè) view恼除,類的名字直接為L(zhǎng)ineChartView踪旷,
如果是代碼創(chuàng)建,也是直接用這個(gè)類創(chuàng)建一個(gè) view 然后設(shè)置 frame 添加到父類 view 上就好了缚柳。
// chartview 本身的屬性設(shè)置
self.chartView.delegate = self;// 代理
self.chartView.backgroundColor = [UIColor clearColor];//設(shè)置背景顏色
self.chartView.descriptionText = @"";//隱藏描述文字
self.chartView.noDataText = @"暫時(shí)沒(méi)有體重?cái)?shù)據(jù)";// 設(shè)置沒(méi)有數(shù)據(jù)的顯示內(nèi)容
self.chartView.legend.enabled = NO;//不顯示圖例說(shuō)明
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;//拖拽后慣性效果的摩擦系數(shù)(0~1)埃脏,數(shù)值越小,慣性越不明顯
self.chartView.rightAxis.enabled = NO;//不繪制右邊軸的信息
self.chartView.leftAxis.enabled = NO;//不繪制左邊軸的信息
self.chartView.xAxis.enabled = NO;
self.chartView.xAxis.drawGridLinesEnabled = NO;//不繪制網(wǎng)絡(luò)線
self.chartView.xAxis.axisLineColor = [UIColor whiteColor];// x 軸的網(wǎng)絡(luò)線顏色
self.chartView.xAxis.labelPosition = XAxisLabelPositionTopInside;// X軸的位置
self.chartView.xAxis.granularity = 1;// 間隔為1
基本上看得懂英文的都懂得這些的意思
看效果圖秋忙,需要點(diǎn)擊的時(shí)候出現(xiàn)一個(gè) view彩掐,跟隨點(diǎn)的位置,charts 本身已經(jīng)實(shí)現(xiàn)了這個(gè)功能灰追,名字叫BalloonMarker堵幽,但是集成的時(shí)候沒(méi)有集成過(guò)來(lái)狗超,需要自己寫(xiě),或者直接在 demo 里面搜索這個(gè)類拷貝到自己的項(xiàng)目中去修改
// 顯示氣泡效果
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;
基礎(chǔ)設(shè)置都設(shè)置好了朴下,接下來(lái)就可以填充數(shù)據(jù)了努咐,charts 你大致是可以理解為這樣的工作模式:
設(shè)置 chartview 的屬性 -> 設(shè)置LineChartData數(shù)據(jù) -> 最后設(shè)置各種數(shù)據(jù)顯示屬性
這是我第一個(gè)坑,有一些屬性是需要等后面數(shù)據(jù)填充了才可以進(jìn)行設(shè)置殴胧,否者會(huì)遇到各種奇怪的問(wèn)題渗稍。
我們繼續(xù)進(jìn)行數(shù)據(jù)的填充吧,chartview 可以設(shè)置多條線团滥,每一條線就是一個(gè)LineChartDataSet竿屹,每一條線上面的點(diǎn)就是ChartDataEntry,想在一條線上顯示多少個(gè)點(diǎn)就需要多少個(gè)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:@""];
//對(duì)于線的各種設(shè)置
set.drawValuesEnabled = NO;//不顯示文字
set.highlightEnabled = YES;//選中拐點(diǎn),是否開(kāi)啟高亮效果(顯示十字線)
set.highlightColor = [UIColor clearColor];// 十字線顏色
set.drawCirclesEnabled = YES;//是否繪制拐點(diǎn)
set.cubicIntensity = 0.2;// 曲線弧度
set.circleRadius = 5.0f;//拐點(diǎn)半徑
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;//是否填充顏色
// 設(shè)置漸變效果
[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];//賦值填充顏色對(duì)象
CGGradientRelease(gradientRef);//釋放gradientRef
// 把線放到LineChartData里面,因?yàn)橹挥幸粭l線拱燃,所以集合里面放一個(gè)就好了,多條線就需要不同的 set 啦
LineChartData *data = [[LineChartData alloc] initWithDataSets:@[set]];
把線都設(shè)置好了力惯,放在 data 里面后碗誉,還需要把 data 放在 chartview 的 data 里面,這樣才算是填充了數(shù)據(jù)
self.chartView.data = [self setData]; // 加載數(shù)據(jù)
之后就進(jìn)行最后的設(shè)置了
//設(shè)置最大顯示值
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;
//設(shè)置最小顯示值
double min = [[arr valueForKeyPath:@"@min.doubleValue"] doubleValue];
self.chartView.leftAxis.axisMinValue = min;
// 設(shè)置區(qū)域顯示父晶,要顯示7個(gè)數(shù)據(jù)哮缺,所以設(shè)置最大顯示和最小顯示
[_chartView setVisibleXRangeMaximum:6];// 最大顯示
[_chartView setVisibleXRangeMinimum:6];// 最小顯示
//設(shè)計(jì)師說(shuō)要數(shù)據(jù)加載的時(shí)候前面留空白3個(gè),拖到后面也是留空白3個(gè)诱建,保證點(diǎn)都是在中間為準(zhǔn)蝴蜓,所以又設(shè)置了留白數(shù)據(jù)
self.chartView.xAxis.axisMinimum = -3;//最前面留空白3個(gè)區(qū)域
self.chartView.xAxis.axisMaximum = self.weights.count+2.1;//后面留空3個(gè)區(qū)域,別問(wèn)我為什么是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:@"目標(biāo)%@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];
//最后每次打開(kāi)都自動(dòng)高亮最后一個(gè)數(shù)據(jù)碟绑,所以需要設(shè)置
FFWeightModel *model = self.weights.lastObject;
[self.chartView highlightValueWithX:self.weights.count-1 y:model.realWeight dataSetIndex:0 callDelegate:NO];
//由于每次打開(kāi)都是顯示在最前面的點(diǎn)俺猿,所以需要移動(dòng)顯示的位置
[_chartView moveViewToX:self.weights.count];// 移動(dòng)到那個(gè)點(diǎn)
//最后顯示出動(dòng)畫(huà)
[self.chartView animateWithYAxisDuration:1];//動(dòng)畫(huà)
其實(shí)以上都是簡(jiǎn)單的設(shè)置,只要花時(shí)間就能設(shè)置出來(lái)格仲,但是難點(diǎn)永遠(yuǎn)不是這些押袍,是那些坑爹的需要,是那些不知道技術(shù)實(shí)現(xiàn)起來(lái)很難以為只是很簡(jiǎn)單的凯肋,所以需求總是天天有谊惭,只能拼命改改改T_T
說(shuō)一些 charts 的一些坑吧
集成BalloonMarker的坑
我是直接拿 demo 的過(guò)來(lái)用的,如果不熟悉 swift 的同學(xué)就要注意了侮东,你把 swift 的文件集成過(guò)來(lái)后圈盔,需要編譯一次,然后你是不是想說(shuō)應(yīng)該怎么聲明這個(gè)BalloonMarker悄雅,因?yàn)槟阍?import 里面沒(méi)有看到這個(gè)驱敲,混編的 swift 其實(shí)全部都在一個(gè)不可見(jiàn)的 h 文件里面,你只需要 #import "你項(xiàng)目的名字-swift.h"這個(gè)文件就可以了宽闲,所有混編的 swift 都會(huì)在這里聲明众眨,不過(guò)你需要先編譯一次才會(huì)在里面可以找到
對(duì)于BalloonMarker修改 view 的背景握牧,他是直接畫(huà)出來(lái)的,但設(shè)計(jì)師的那個(gè)是切圖出來(lái)娩梨,所以你需要在BalloonMarker里面找到open override func draw(context: CGContext, point: CGPoint)
這個(gè)方法沿腰,然后刪除或者注釋掉那個(gè)很長(zhǎng)的屬性if let color = color{}
,新建了一個(gè)屬性叫open var img: UIImage?
然后直接在被你刪掉或者注釋掉的地方添加
if let img = img{
img.draw(at: CGPoint(x:rect.origin.x-2,y:rect.origin.y))
}
編譯一次后你就可以直接在外部調(diào)用 img 這個(gè)屬性并且賦值了
數(shù)據(jù)源的坑
如果你需要顯示中文狈定,恭喜來(lái)這個(gè)坑颂龙,目前我沒(méi)找到什么好的解決辦法
比如想顯示 x 軸的文字,就需要self.chartView.xAxis.valueFormatter = self;
并且實(shí)現(xiàn)它的數(shù)據(jù)源代理就可以用他的方法了纽什,- (NSString * _Nonnull)stringForValue:(double)value axis:(ChartAxisBase * _Nullable)axis;
目前沒(méi)有很好的利用這個(gè)方法厘托,覺(jué)得不好用
代理的坑
charts 主要用到有三個(gè)代理
@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)
第一個(gè)是點(diǎn)擊后響應(yīng)的方法,第二個(gè)是點(diǎn)擊同一個(gè)點(diǎn)響應(yīng)的方法稿湿,第三個(gè)是拖動(dòng)就會(huì)響應(yīng)的方法
先說(shuō)第一個(gè)吧铅匹,點(diǎn)擊后根據(jù)你點(diǎn)擊的 x 的位置定位到你點(diǎn)擊的那個(gè)點(diǎn),并且高亮顯示饺藤,如果你有用 markerview 就會(huì)顯示出來(lái)包斑,包括高亮線,由于我用不到涕俗,所以就設(shè)置高亮線為透明
我這邊的要求是罗丰,不管點(diǎn)擊哪個(gè)點(diǎn),都要自動(dòng)移動(dòng)到中間再姑,所以需要取出你點(diǎn)擊的點(diǎn)萌抵,然后移動(dòng)到中間
[self.chartView moveViewToAnimatedWithXValue:entry.x-3 yValue:0 axis:AxisDependencyLeft duration:.3];
相對(duì)來(lái)說(shuō)這個(gè)是很容易實(shí)現(xiàn)的,畢竟你點(diǎn)擊后能夠得到對(duì)于的ChartDataEntry的值元镀,所以算出你點(diǎn)擊的位置然后設(shè)置在中間都是很容易的绍填,但是滑動(dòng)就難了,滑動(dòng)的時(shí)候需要保證每次滑動(dòng)都是在中間栖疑,隨便說(shuō)下讨永,滑動(dòng)方法里面的 dx 和 dy 是在你每次手指滑動(dòng)的時(shí)候重新開(kāi)始計(jì)算的,拿 dx 來(lái)說(shuō)遇革,你滑動(dòng)左邊數(shù)值是從0開(kāi)始加上去的卿闹,右邊滑動(dòng)是從0到負(fù)數(shù)的疊加,還好 chartview 有兩個(gè)屬性可以用來(lái)做判斷锻霎,分別是
self.chartView.highestVisibleX
self.chartView.lowestVisibleX
取NSInteger index = self.chartView.highestVisibleX-3;
拿到 index
然后顯示高亮狀態(tài)[self.chartView highlightValueWithX:index y:model.realWeight dataSetIndex:0 callDelegate:NO];
但是這樣還是不行,畢竟滑動(dòng)不會(huì)自動(dòng)居中揪漩,所以就導(dǎo)致顯示的點(diǎn)可能會(huì)不在中間旋恼,所以自己改源碼增加了一個(gè)滑動(dòng)結(jié)束的代理
高亮的坑
安卓那邊高亮顯示可以直接用
- (void)highlightValueWithX:(double)x dataSetIndex:(NSInteger)dataSetIndex;
但是 iOS 這邊直接用這個(gè)方法是不行的,在 github 上也有人問(wèn)過(guò)此類問(wèn)題审葬,最后只能用另一個(gè)屬性方法
- (void)highlightValueWithX:(double)x y:(double)y dataSetIndex:(NSInteger)dataSetIndex;
但由于我在滑動(dòng)代理里面增加了高亮的方法深滚,高亮方法默認(rèn)是打開(kāi)代理的狀態(tài),這樣會(huì)導(dǎo)致滑動(dòng)出錯(cuò)涣觉,所以需要用另一個(gè)高亮顯示的方法
- (void)highlightValueWithX:(double)x y:(double)y dataSetIndex:(NSInteger)dataSetIndex callDelegate:(BOOL)callDelegate;
設(shè)置代理為 NO 就可以了
總結(jié)
其實(shí)到了最后跟設(shè)計(jì)圖還是有很多不同的痴荐,放出實(shí)際圖,其實(shí)多少辛酸淚不是簡(jiǎn)單兩語(yǔ)能夠說(shuō)得清道得明官册,排版有點(diǎn)亂生兆,大家請(qǐng)將就下哈~這是第一版,希望以后再努力的去改一改吧膝宁!