前言
之前就听过charts这个流行的图表库,而现在工程中要运用到这个第三方库。库中定义的属性还是挺多,对它的运用着实让我头大了几天。再加上是用swift写的,有点难理解。
自己在网上也查过,觉得这二篇文章对自己图表的运用帮助挺大的。
iOS源码解读-Charts图表1:了解基类(ChartViewBase、AxisBase(这篇让我对里面的属性有了个大致的理解)
ChartsUnderstandAndUsage(这个的用法比较清晰,便于运用)
探究
为了知道图表的绘制过程,自己单步运行查看。先摸索柱状图的原理
一般运用的话就是4步走:
//添加柱状图
func addBarChartView(){
barChartView.backgroundColor = ZHFColor.white
barChartView.frame.size = CGSize.init(width: ScreenWidth - 20, height: 300)
barChartView.center = self.view.center
barChartView.delegate = self
self.view.addSubview(barChartView)
//刷新按钮响应
refreshrBtn.addTarget(self, action: #selector(updataData), for: UIControlEvents.touchUpInside)
}
func setBarChartViewBaseStyle(){
//基本样式
barChartView.noDataText = "暂无数据"//没有数据时的显示
barChartView.drawValueAboveBarEnabled = true//数值显示是否在条柱上面
barChartView.drawBarShadowEnabled = false//是否绘制阴影背景
//交互设置 (把煮食逐个取消试试)
// barChartView.scaleXEnabled = false//是否X轴缩放,拖动图表时x轴上的值不会变
barChartView.scaleYEnabled = false //是否Y轴缩放
barChartView.doubleTapToZoomEnabled = true//是否双击是否缩放
// barChartView.pinchZoomEnabled = false//取消XY轴是否同时缩放
barChartView.dragEnabled = true //是否启用拖拽图表
barChartView.dragDecelerationEnabled = true //拖拽后是否有惯性效果
barChartView.dragDecelerationFrictionCoef = 0.9 //拖拽后惯性效果的摩擦系数(0~1),数值越小,惯性越不明显
}
func setBarChartViewXY(){
//1.X轴样式设置(对应界面显示的--->0月到7月)
let xAxis: XAxis = barChartView.xAxis
xAxis.valueFormatter = self //重写代理方法 设置x轴数据
xAxis.axisLineWidth = 1 //设置X轴线宽
xAxis.labelPosition = XAxis.LabelPosition.bottom //X轴(5种位置显示,根据需求进行设置)
xAxis.drawGridLinesEnabled = true//不绘制网格
xAxis.axisLineColor = UIColor.red //x轴颜色
xAxis.labelWidth = 1 //设置label间隔,若设置为1,则如果能全部显示,则每个柱形下面都会显示label
xAxis.labelFont = UIFont.systemFont(ofSize: 10)//x轴数值字体大小
xAxis.labelTextColor = ZHFColor.brown//数值字体颜色
xAxis.gridLineWidth = 1.0 //网格线
xAxis.gridColor = UIColor.orange //网络线颜色
xAxis.labelRotationAngle = 45 //旋转角度
//2.Y轴左样式设置(对应界面显示的--->0 到 100)
let leftAxisFormatter = NumberFormatter()
leftAxisFormatter.minimumFractionDigits = 0
leftAxisFormatter.maximumFractionDigits = 1
leftAxisFormatter.positiveSuffix = " $" //数字前缀positivePrefix、 后缀positiveSuffix
let leftAxis: YAxis = barChartView.leftAxis
leftAxis.valueFormatter = DefaultAxisValueFormatter.init(formatter: leftAxisFormatter)
leftAxis.axisMinimum = 0 //最小值
// leftAxis.axisMaximum = axisMaximum //最大值
leftAxis.forceLabelsEnabled = true //不强制绘制制定数量的label
leftAxis.labelCount = 6 //Y轴label数量,数值不一定,如果forceLabelsEnabled等于true, 则强制绘制制定数量的label, 但是可能不平均
leftAxis.inverted = false //是否将Y轴进行上下翻转
leftAxis.axisLineWidth = 0.5 //Y轴线宽
leftAxis.axisLineColor = UIColor.red //Y轴颜色
leftAxis.labelPosition = YAxis.LabelPosition.outsideChart//坐标数值的位置
leftAxis.labelTextColor = ZHFColor.brown//坐标数值字体颜色
leftAxis.labelFont = UIFont.systemFont(ofSize: 10) //y轴字体大小
//设置虚线样式的网格线(对应的是每条横着的虚线[10.0, 3.0]对应实线和虚线的长度)
leftAxis.drawGridLinesEnabled = true //是否绘制网格线(默认为true)
leftAxis.gridLineDashLengths = [10.0, 3.0] //线段类型
leftAxis.gridColor = UIColor.purple //网格线颜色
leftAxis.gridAntialiasEnabled = true//开启抗锯齿
leftAxis.spaceTop = 0.15//最大值到顶部的范围比
//设置限制线
let limitLine : ChartLimitLine = ChartLimitLine.init(limit: Double(axisMaximum * 0.85), label: "限制线")
limitLine.lineWidth = 2
limitLine.lineColor = ZHFColor.green
limitLine.lineDashLengths = [5.0, 2.0]
limitLine.labelPosition = ChartLimitLine.LabelPosition.rightTop//位置
limitLine.valueTextColor = ZHFColor.zhf66_contentTextColor
limitLine.valueFont = UIFont.systemFont(ofSize: 12)
leftAxis.addLimitLine(limitLine)
leftAxis.drawLimitLinesBehindDataEnabled = false //设置限制线在柱线图后面(默认在前)
//3.Y轴右样式设置(如若设置可参考左样式)
barChartView.rightAxis.enabled = false //不绘制右边轴线
let rightAxis: YAxis = barChartView.rightAxis
rightAxis.gridColor = UIColor.blue
rightAxis.gridLineWidth = 1.0
rightAxis.valueFormatter = DefaultAxisValueFormatter.init(formatter: leftAxisFormatter)
rightAxis.axisMinimum = 0 //最小值
// rightAxis.axisMaximum = 100 //最大值
rightAxis.forceLabelsEnabled = true //不强制绘制制定数量的label
rightAxis.labelCount = 6
//4.描述文字设置
barChartView.chartDescription?.text = "柱形图"//右下角的description文字样式 不设置的话会有默认数据
barChartView.chartDescription?.position = CGPoint.init(x: 80, y: 5)//位置(及在barChartView的中心点)
barChartView.chartDescription?.font = UIFont.systemFont(ofSize: 12)//大小
barChartView.chartDescription?.textColor = ZHFColor.orange
//5.设置类型试图的对齐方式,右上角 (默认左下角)
let legend = barChartView.legend
legend.enabled = true
legend.horizontalAlignment = .right
legend.verticalAlignment = .top
legend.orientation = .horizontal
legend.textColor = ZHFColor.orange
legend.font = UIFont.systemFont(ofSize: 11.0)
}
//设置数据
@objc func updataData(){
//对应x轴上面需要显示的数据
let count = 1
let x1Vals: NSMutableArray = NSMutableArray.init()
for i in 0 ..< count {
//x轴字体展示
x1Vals.add("\(i)月")
self.xVals = x1Vals
}
//对应Y轴上面需要显示的数据
let yVals: NSMutableArray = NSMutableArray.init()
let arr:[Int] = [5,3,7]
for i in 0 ..< count {
// let val: Double = Double(arc4random_uniform(UInt32(axisMaximum)))
let val:Double = Double(arr[I])
let entry:BarChartDataEntry = BarChartDataEntry.init(x: Double(i), y: Double(val))
yVals.add(entry)
}
//创建BarChartDataSet对象,其中包含有Y轴数据信息,以及可以设置柱形样式
let set1: BarChartDataSet = BarChartDataSet.init(values: yVals as? [ChartDataEntry], label: "信息")
set1.barBorderWidth = 0.2 //边线宽
set1.drawValuesEnabled = true //是否在柱形图上面显示数值
set1.highlightEnabled = true //点击选中柱形图是否有高亮效果,(单击空白处取消选中)
set1.drawIconsEnabled = true
// set1.setColors(ZHFColor.gray,ZHFColor.green,ZHFColor.yellow,ZHFColor.zhf_randomColor(),ZHFColor.zhf_randomColor())//设置柱形图颜色(是一个循环,例如:你设置5个颜色,你设置8个柱形,后三个对应的颜色是该设置中的前三个,依次类推)
// set1.setColors(ChartColorTemplates.material(), alpha: 1)
set1.setColor(UIColor.red)//颜色一致
let dataSets: NSMutableArray = NSMutableArray.init()
dataSets.add(set1)
//创建BarChartData对象, 此对象就是barChartView需要最终数据对象
let data: BarChartData = BarChartData.init(dataSets: dataSets as? [IChartDataSet])
data.barWidth = 0.5 //默认是0.85 (介于0-1之间)
data.setValueFont(UIFont.systemFont(ofSize: 10))
data.setValueTextColor(ZHFColor.orange)
print("\(data.xMax) \(data.xMin) \(data.yMax) \(data.yMin)")
let formatter: NumberFormatter = NumberFormatter.init()
formatter.numberStyle = NumberFormatter.Style.currency//自定义数据显示格式 小数点形式(可以尝试不同看效果)
let forma :DefaultValueFormatter = DefaultValueFormatter.init(formatter: formatter)
data.setValueFormatter(forma)
barChartView.data = data
barChartView.animate(yAxisDuration: 1)//展示方式xAxisDuration 和 yAxisDuration两种
// barChartView.animate(xAxisDuration: 2, yAxisDuration: 2)//展示方式xAxisDuration 和 yAxisDuration两种
}
}
//MARK:-
extension BarChartVC :ChartViewDelegate,IAxisValueFormatter {
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let xStr = self.xVals[Int(value)] as! String
print("xvalue ==== \(xStr)")
return xStr
}
//1.点击选中
func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
ZHFLog(message: "点击选中")
}
//2.没有选中
func chartValueNothingSelected(_ chartView: ChartViewBase) {
ZHFLog(message: "没有选中")
}
//3.捏合放大或缩小
func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) {
ZHFLog(message: "捏合放大或缩小")
}
//4.拖拽图表
func chartTranslated(_ chartView: ChartViewBase, dX: CGFloat, dY: CGFloat) {
ZHFLog(message: "拖拽图表")
}
}
ChartsUnderstandAndUsage demo中有详细的注释。
1.1红色柱子的绘制方法
前期通过各种属性来算位置,offset等。
在BarChartRenderer中有drawData方法
再调用drawDataSet
open override func drawData(context: CGContext)
{
guard
let dataProvider = dataProvider,
let barData = dataProvider.barData
else { return }
for i in 0 ..< barData.dataSetCount
{
guard let set = barData.getDataSetByIndex(i) else { continue }
if set.isVisible
{
if !(set is IBarChartDataSet)
{
fatalError("Datasets for BarChartRenderer must conform to IBarChartDataset")
}
drawDataSet(context: context, dataSet: set as! IBarChartDataSet, index: i)
}
}
}
在drawdataSet方法,画柱子的方法,经过buffer的操作rect在它的rects数组中,用 context.fill(barRect)就可以绘出红色的柱子了
这里只分析XAxisRenderer.swift
,也就是X轴的绘制过程,x轴为什么会出现0等。
在barChartView.data = data
后,在设置方法中调用 notifyDataSetChanged()
/// The data for the chart
open var data: ChartData?
{
get
{
return _data
}
set
{
_data = newValue
_offsetsCalculated = false
guard let _data = _data else
{
setNeedsDisplay()
return
}
// calculate how many digits are needed
setupDefaultFormatter(min: _data.getYMin(), max: _data.getYMax())
for set in _data.dataSets
{
if set.needsFormatter || set.valueFormatter === _defaultValueFormatter
{
set.valueFormatter = _defaultValueFormatter
}
}
// let the chart know there is new data
notifyDataSetChanged()
}
}
再调用
open override func notifyDataSetChanged()
{
renderer?.initBuffers()
calcMinMax() //算出数据中的最大值,最小值包括x轴,y轴
leftYAxisRenderer.computeAxis(min: leftAxis._axisMinimum, max: leftAxis._axisMaximum, inverted: leftAxis.isInverted)
rightYAxisRenderer.computeAxis(min: rightAxis._axisMinimum, max: rightAxis._axisMaximum, inverted: rightAxis.isInverted)
if let data = _data
{
xAxisRenderer.computeAxis(
min: _xAxis._axisMinimum,
max: _xAxis._axisMaximum,
inverted: false)
if _legend !== nil
{
legendRenderer?.computeLegend(data: data)
}
}
calculateOffsets()
setNeedsDisplay()
}
总结
charts画图表到是挺方便的,但如果是要修改UI话,源码又太难改,复杂的位置计算。