Ios swift中的对数y轴?
我正在开发一个有很多数字的金融应用程序 例如,我的一张图表正在从25000美元增长到200000000美元(指数增长) 正因为如此,使用任何没有对数刻度的规则图表都会导致我的大部分数据简单地“平直”,而事实上数据增长很快 这里有一个例子 (忽略尚未设置) 上面的折线图是使用 老实说,我现在还不能完全确定我是否可以问这样的问题。但我不知道从哪里开始 经过几个小时的搜索,我找不到任何库、教程、示例应用程序或任何与IOS相关的以对数轴(X或Y)为特征的图形 在我找不到任何东西之后,我意识到我必须自己实现这个功能。我找到了最适合我的应用程序的库,并且我一直在尝试如何实现一种方法,以允许y刻度以对数刻度(10的幂)进行缩放 我对原木秤的工作原理做了不少研究,并在我的大学里大量使用了它们。但我从来没有见过一个编程的例子 我发现一个是用JavaScript写的(我想)。但不幸的是,我没有web开发经验,无法正确翻译:( 我真的不喜欢把别人的代码放在上面,但我真的不知道从哪里开始 如果这是不允许的,请发表评论,我会立即处理它 这一切都归功于这个家伙: 下面是我认为需要实现日志规模的代码Ios swift中的对数y轴?,ios,swift,linechart,logarithm,Ios,Swift,Linechart,Logarithm,我正在开发一个有很多数字的金融应用程序 例如,我的一张图表正在从25000美元增长到200000000美元(指数增长) 正因为如此,使用任何没有对数刻度的规则图表都会导致我的大部分数据简单地“平直”,而事实上数据增长很快 这里有一个例子 (忽略尚未设置) 上面的折线图是使用 老实说,我现在还不能完全确定我是否可以问这样的问题。但我不知道从哪里开始 经过几个小时的搜索,我找不到任何库、教程、示例应用程序或任何与IOS相关的以对数轴(X或Y)为特征的图形 在我找不到任何东西之后,我意识到我必须自己
private func drawChart() {
drawingHeight = bounds.height - bottomInset - topInset
drawingWidth = bounds.width
let minMax = getMinMax()
min = minMax.min
max = minMax.max
highlightShapeLayer = nil
// Remove things before drawing, e.g. when changing orientation
for view in self.subviews {
view.removeFromSuperview()
}
for layer in layerStore {
layer.removeFromSuperlayer()
}
layerStore.removeAll()
// Draw content
for (index, series) in enumerate(self.series) {
// Separate each line in multiple segments over and below the x axis
var segments = Chart.segmentLine(series.data as ChartLineSegment)
for (i, segment) in enumerate(segments) {
let scaledXValues = scaleValuesOnXAxis( segment.map( { return $0.x } ) )
let scaledYValues = scaleValuesOnYAxis( segment.map( { return $0.y } ) )
if series.line {
drawLine(xValues: scaledXValues, yValues: scaledYValues, seriesIndex: index)
}
if series.area {
drawArea(xValues: scaledXValues, yValues: scaledYValues, seriesIndex: index)
}
}
}
drawAxes()
if xLabels != nil || series.count > 0 {
drawLabelsAndGridOnXAxis()
}
if yLabels != nil || series.count > 0 {
drawLabelsAndGridOnYAxis()
}
}
// MARK: - Scaling
private func getMinMax() -> (min: ChartPoint, max: ChartPoint) {
// Start with user-provided values
var min = (x: minX, y: minY)
var max = (x: maxX, y: maxY)
// Check in datasets
for series in self.series {
let xValues = series.data.map( { (point: ChartPoint) -> Float in
return point.x } )
let yValues = series.data.map( { (point: ChartPoint) -> Float in
return point.y } )
let newMinX = minElement(xValues)
let newMinY = minElement(yValues)
let newMaxX = maxElement(xValues)
let newMaxY = maxElement(yValues)
if min.x == nil || newMinX < min.x! { min.x = newMinX }
if min.y == nil || newMinY < min.y! { min.y = newMinY }
if max.x == nil || newMaxX > max.x! { max.x = newMaxX }
if max.y == nil || newMaxY > max.y! { max.y = newMaxY }
}
// Check in labels
if xLabels != nil {
let newMinX = minElement(xLabels!)
let newMaxX = maxElement(xLabels!)
if min.x == nil || newMinX < min.x { min.x = newMinX }
if max.x == nil || newMaxX > max.x { max.x = newMaxX }
}
if yLabels != nil {
let newMinY = minElement(yLabels!)
let newMaxY = maxElement(yLabels!)
if min.y == nil || newMinY < min.y { min.y = newMinY }
if max.y == nil || newMaxY > max.y { max.y = newMaxY }
}
if min.x == nil { min.x = 0 }
if min.y == nil { min.y = 0 }
if max.x == nil { max.x = 0 }
if max.y == nil { max.y = 0 }
return (min: (x: min.x!, y: min.y!), max: (x: max.x!, max.y!))
}
private func scaleValuesOnXAxis(values: Array<Float>) -> Array<Float> {
let width = Float(drawingWidth)
var factor: Float
if max.x - min.x == 0 { factor = 0 }
else { factor = width / (max.x - min.x) }
let scaled = values.map { factor * ($0 - self.min.x) }
return scaled
}
private func scaleValuesOnYAxis(values: Array<Float>) -> Array<Float> {
let height = Float(drawingHeight)
var factor: Float
if max.y - min.y == 0 { factor = 0 }
else { factor = height / (max.y - min.y) }
let scaled = values.map { Float(self.topInset) + height - factor * ($0 - self.min.y) }
return scaled
}
private func scaleValueOnYAxis(value: Float) -> Float {
let height = Float(drawingHeight)
var factor: Float
if max.y - min.y == 0 { factor = 0 }
else { factor = height / (max.y - min.y) }
let scaled = Float(self.topInset) + height - factor * (value - min.y)
return scaled
}
private func getZeroValueOnYAxis() -> Float {
if min.y > 0 {
return scaleValueOnYAxis(min.y)
}
else {
return scaleValueOnYAxis(0)
}
}
// MARK: - Drawings
private func isVerticalSegmentAboveXAxis(yValues: Array<Float>) -> Bool {
// YValues are "reverted" from top to bottom, so min is actually the maxz
let min = maxElement(yValues)
let zero = getZeroValueOnYAxis()
return min <= zero
}
private func drawLine(#xValues: Array<Float>, yValues: Array<Float>, seriesIndex: Int) -> CAShapeLayer {
let isAboveXAxis = isVerticalSegmentAboveXAxis(yValues)
let path = CGPathCreateMutable()
CGPathMoveToPoint(path, nil, CGFloat(xValues.first!), CGFloat(yValues.first!))
for i in 1..<yValues.count {
let y = yValues[i]
CGPathAddLineToPoint(path, nil, CGFloat(xValues[i]), CGFloat(y))
}
var lineLayer = CAShapeLayer()
lineLayer.frame = self.bounds
lineLayer.path = path
if isAboveXAxis {
lineLayer.strokeColor = series[seriesIndex].colors.above.CGColor
}
else {
lineLayer.strokeColor = series[seriesIndex].colors.below.CGColor
}
lineLayer.fillColor = nil
lineLayer.lineWidth = lineWidth
lineLayer.lineJoin = kCALineJoinBevel
self.layer.addSublayer(lineLayer)
layerStore.append(lineLayer)
return lineLayer
}
private func drawArea(#xValues: Array<Float>, yValues: Array<Float>, seriesIndex: Int) {
let isAboveXAxis = isVerticalSegmentAboveXAxis(yValues)
let area = CGPathCreateMutable()
let zero = CGFloat(getZeroValueOnYAxis())
CGPathMoveToPoint(area, nil, CGFloat(xValues[0]), zero)
for i in 0..<xValues.count {
CGPathAddLineToPoint(area, nil, CGFloat(xValues[i]), CGFloat(yValues[i]))
}
CGPathAddLineToPoint(area, nil, CGFloat(xValues.last!), zero)
var areaLayer = CAShapeLayer()
areaLayer.frame = self.bounds
areaLayer.path = area
areaLayer.strokeColor = nil
if isAboveXAxis {
areaLayer.fillColor = series[seriesIndex].colors.above.colorWithAlphaComponent(areaAlphaComponent).CGColor
}
else {
areaLayer.fillColor = series[seriesIndex].colors.below.colorWithAlphaComponent(areaAlphaComponent).CGColor
}
areaLayer.lineWidth = 0
self.layer.addSublayer(areaLayer)
layerStore.append(areaLayer)
}
private func drawAxes() {
let context = UIGraphicsGetCurrentContext()
CGContextSetStrokeColorWithColor(context, axesColor.CGColor)
CGContextSetLineWidth(context, 0.5)
// horizontal axis at the bottom
CGContextMoveToPoint(context, 0, drawingHeight + topInset)
CGContextAddLineToPoint(context, drawingWidth, drawingHeight + topInset)
CGContextStrokePath(context)
// horizontal axis at the top
CGContextMoveToPoint(context, 0, 0)
CGContextAddLineToPoint(context, drawingWidth, 0)
CGContextStrokePath(context)
// horizontal axis when y = 0
if min.y < 0 && max.y > 0 {
let y = CGFloat(getZeroValueOnYAxis())
CGContextMoveToPoint(context, 0, y)
CGContextAddLineToPoint(context, drawingWidth, y)
CGContextStrokePath(context)
}
// vertical axis on the left
CGContextMoveToPoint(context, 0, 0)
CGContextAddLineToPoint(context, 0, drawingHeight + topInset)
CGContextStrokePath(context)
// vertical axis on the right
CGContextMoveToPoint(context, drawingWidth, 0)
CGContextAddLineToPoint(context, drawingWidth, drawingHeight + topInset)
CGContextStrokePath(context)
}
private func drawChart(){
绘图高度=边界高度-底部插图-顶部插图
drawingWidth=bounds.width
设minMax=getMinMax()
最小值=最小值最大值最小值
max=minMax.max
highlightShapeLayer=零
//在绘图前(例如更改方向时)移除物品
用于在self.subview中查看{
view.removeFromSuperview()视图
}
用于layerStore中的图层{
layer.removeFromSuperlayer()
}
layerStore.removeAll()
//绘制内容
用于枚举(self.series)中的(索引,序列){
//在x轴上方和下方的多个线段中分隔每条线
var segments=Chart.segmentLine(series.data作为ChartLineSegment)
枚举(段)中的(i,段){
设scaledXValues=scaleValuesOnXAxis(segment.map({return$0.x}))
设scaledYValues=scaleValuesOnYAxis(segment.map({return$0.y}))
if系列.line{
绘制线(X值:缩放X值,Y值:缩放Y值,系列索引:索引)
}
if系列面积{
绘图区域(X值:缩放X值,Y值:缩放Y值,系列索引:索引)
}
}
}
牵引轴()
如果xLabels!=nil | | series.count>0{
drawLabelsAndGridOnXAxis()
}
如果yLabels!=nil | | series.count>0{
drawLabelsAndGridOnYAxis()
}
}
//标记:-缩放
私有函数getMinMax()->(最小值:ChartPoint,最大值:ChartPoint){
//从用户提供的值开始
变量min=(x:minX,y:minY)
变量max=(x:maxX,y:maxY)
//签入数据集
对于self.series中的系列{
让xValues=series.data.map({(point:ChartPoint)->浮入
返回点(x})
让yValues=series.data.map({(点:ChartPoint)->浮入
返回点(y})
设newMinX=minElement(xValues)
设newMinY=minElement(y值)
设newMaxX=maxElement(xValues)
设newMaxY=maxElement(yValue)
如果min.x==nil | | newMinXmax.x!{max.x=newMaxX}
如果max.y==nil | | newMaxY>max.y!{max.y=newMaxY}
}
//签入标签
如果xLabels!=nil{
让newMinX=minElement(xLabels!)
设newMaxX=maxElement(xLabels!)
如果min.x==nil | | newMinXmax.x{max.x=newMaxX}
}
如果为零{
让newMinY=minElement(yLabels!)
设newMaxY=maxElement(yLabels!)
如果min.y==nil | | newMinYmax.y{max.y=newMaxY}
}
如果min.x==nil{min.x=0}
如果min.y==nil{min.y=0}
如果max.x==nil{max.x=0}
如果max.y==nil{max.y=0}
返回值(min:(x:min.x!,y:min.y!),max:(x:max.x!,max.y!))
}
私有函数scaleValuesOnXAxis(值:数组)->Array{
让宽度=浮动(drawingWidth)
风险系数:浮动
如果max.x-min.x==0{factor=0}
else{系数=宽度/(max.x-min.x)}
设scaled=values.map{factor*($0-self.min.x)}
返回标度
}
私有函数scaleValuesOnYAxis(值:数组)->Array{
让高度=浮动(图纸高度)
风险系数:浮动
如果max.y-min.y==0{factor=0}
else{系数=高度/(最大y-最小y)}
设scaled=values.map{Float(self.topInset)+height-factor*($0-self.min.y)}
返回标度
}
私有函数scaleValueOnAxis(值:Float)->Float{
让高度=浮动(图纸高度)
风险系数:浮动
如果max.y-min.y==0{factor=0}
else{系数=高度/(最大y-最小y)}
让缩放=浮动(自顶插图)+高度-系数*(值-最小y)
返回标度
}
private func getZeroValueOnYAxis()->Float{
如果最小y>0{
返回刻度值轴(最小y)
}
否则{
返回ScaleValueOnAxis(0)
}
}
//标记:-图纸
私有函数是X轴上的垂直分段(Y值:数组)->Bool{
//Y值从上到下“还原”,因此min实际上是maxz