diff --git a/Sources/FioriCharts/Charts/BarChart/BarChart.swift b/Sources/FioriCharts/Charts/BarChart/BarChart.swift index 71f40b789..1e50b450c 100644 --- a/Sources/FioriCharts/Charts/BarChart/BarChart.swift +++ b/Sources/FioriCharts/Charts/BarChart/BarChart.swift @@ -199,7 +199,7 @@ class BarAxisDataSource: DefaultAxisDataSource { var startIndex = Int(startPosY / unitHeight).clamp(low: 0, high: maxDataCount - 1) var startOffset = unitHeight * CGFloat(startIndex) - startPosY - if abs(startOffset) >= clusterHeight { + if abs(startOffset) >= clusterHeight && startIndex < maxDataCount - 1 { startIndex += 1 startOffset = unitHeight * CGFloat(startIndex) - startPosY } diff --git a/Sources/FioriCharts/Charts/ColumnChart/ColumnChart.swift b/Sources/FioriCharts/Charts/ColumnChart/ColumnChart.swift index 149963309..4c2252a21 100644 --- a/Sources/FioriCharts/Charts/ColumnChart/ColumnChart.swift +++ b/Sources/FioriCharts/Charts/ColumnChart/ColumnChart.swift @@ -168,7 +168,7 @@ class ColumnAxisDataSource: DefaultAxisDataSource { var startIndex = Int(model.startPos.x / unitWidth).clamp(low: 0, high: maxDataCount - 1) var startOffset = unitWidth * CGFloat(startIndex) - model.startPos.x - if abs(startOffset) >= clusterWidth { + if abs(startOffset) >= clusterWidth && startIndex < maxDataCount - 1 { startIndex += 1 startOffset = unitWidth * CGFloat(startIndex) - model.startPos.x } diff --git a/Sources/FioriCharts/Charts/Common/AxisDataSource.swift b/Sources/FioriCharts/Charts/Common/AxisDataSource.swift index 0e06eba87..9474d0bf3 100644 --- a/Sources/FioriCharts/Charts/Common/AxisDataSource.swift +++ b/Sources/FioriCharts/Charts/Common/AxisDataSource.swift @@ -295,14 +295,13 @@ class DefaultAxisDataSource: AxisDataSource { } func displayCategoryIndexesAndOffsets(_ model: ChartModel, rect: CGRect) -> (startIndex: Int, endIndex: Int, startOffset: CGFloat, endOffset: CGFloat) { - //return (0, 0, 0, 0) let width = rect.size.width let startPosIn = model.startPos.x + let maxDataCount = model.numOfCategories(in: 0) + let unitWidth: CGFloat = max(width * model.scale / CGFloat(max(maxDataCount - 1, 1)), 1) + let startIndex = Int(startPosIn / unitWidth).clamp(low: 0, high: maxDataCount - 1) - let unitWidth: CGFloat = max(width * model.scale / CGFloat(max(ChartUtility.numOfDataItems(model) - 1, 1)), 1) - let startIndex = Int(startPosIn / unitWidth) - - var endIndex = Int(((startPosIn + width) / unitWidth).rounded(.up)) + var endIndex = Int(((startPosIn + width) / unitWidth).rounded(.up)).clamp(low: 0, high: maxDataCount - 1) let startOffset: CGFloat = -startPosIn.truncatingRemainder(dividingBy: unitWidth) let endOffset: CGFloat = (CGFloat(endIndex) * unitWidth - startPosIn - width).truncatingRemainder(dividingBy: unitWidth) diff --git a/Sources/FioriCharts/Charts/Common/XAxisView.swift b/Sources/FioriCharts/Charts/Common/XAxisView.swift index 314a54be0..30d038969 100644 --- a/Sources/FioriCharts/Charts/Common/XAxisView.swift +++ b/Sources/FioriCharts/Charts/Common/XAxisView.swift @@ -10,15 +10,12 @@ import SwiftUI struct XAxisView: View { @EnvironmentObject var model: ChartModel + @Environment(\.axisDataSource) var axisDataSource + let isShowBaselineOnly: Bool let isShowLabelsOnly: Bool - weak var axisDataSource: AxisDataSource? = nil - - @State private var xAxisSize: CGSize = CGSize(width: 0, height: 24) - - init(axisDataSource: AxisDataSource? = nil, isShowBaselineOnly: Bool = false, isShowLabelsOnly: Bool = false) { - self.axisDataSource = axisDataSource + init(isShowBaselineOnly: Bool = false, isShowLabelsOnly: Bool = false) { self.isShowBaselineOnly = isShowBaselineOnly self.isShowLabelsOnly = isShowLabelsOnly } @@ -30,10 +27,7 @@ struct XAxisView: View { } func makeBody(in rect: CGRect) -> some View { - var xAxisLabels: [AxisTitle] = [] - if let res = axisDataSource?.xAxisLabels(model, rect: rect) { - xAxisLabels = res - } + let xAxisLabels: [AxisTitle] = axisDataSource.xAxisLabels(model, rect: rect) var baselineYPos: CGFloat = model.categoryAxis.baseline.width / 2 var labelYPos: CGFloat = model.categoryAxis.baseline.width + 3 + (rect.size.height - model.categoryAxis.baseline.width - 3) / 2 @@ -48,18 +42,16 @@ struct XAxisView: View { } let axis = model.chartType == .bar || model.chartType == .stackedBar ? model.numericAxis : model.categoryAxis - + return ZStack { - if !xAxisLabels.isEmpty && (axisDataSource?.isEnoughSpaceToShowXAxisLables ?? true) && !axis.labels.isHidden && !isShowBaselineOnly { - ForEach(xAxisLabels) { title in - if !axis.labels.isHidden { - // category labels - Text(title.title) - .font(.system(size: axis.labels.fontSize)) - .foregroundColor(axis.labels.color) - .frame(maxWidth: rect.size.width / 2) - .position(x: title.pos.x, y: labelYPos) - } + if !xAxisLabels.isEmpty && axisDataSource.isEnoughSpaceToShowXAxisLables && !axis.labels.isHidden && !isShowBaselineOnly { + ForEach(xAxisLabels) { label in + // category labels + Text(label.title) + .font(.system(size: axis.labels.fontSize)) + .foregroundColor(axis.labels.color) + .frame(maxWidth: rect.size.width / 2) + .position(x: label.pos.x, y: labelYPos) } } @@ -84,13 +76,17 @@ struct XAxisView_Previews: PreviewProvider { return Group { ForEach(Tests.lineModels) { - XAxisView(axisDataSource: axisDataSource).environmentObject($0) + XAxisView() + .environmentObject($0) + .environment(\.axisDataSource, axisDataSource) } .frame(width: 300, height: 20, alignment: .topLeading) .previewLayout(.sizeThatFits) ForEach(Tests.stockModels) { - XAxisView(axisDataSource: axisStockDataSource).environmentObject($0) + XAxisView() + .environmentObject($0) + .environment(\.axisDataSource, axisStockDataSource) } .frame(width: 300, height: 20, alignment: .topLeading) .previewLayout(.sizeThatFits) diff --git a/Sources/FioriCharts/Charts/Common/XYAxisChart.swift b/Sources/FioriCharts/Charts/Common/XYAxisChart.swift index 43cd73df0..99896133f 100644 --- a/Sources/FioriCharts/Charts/Common/XYAxisChart.swift +++ b/Sources/FioriCharts/Charts/Common/XYAxisChart.swift @@ -146,10 +146,10 @@ struct XYAxisChart: View { GridLinesAndChartView(chartView: chartView, indicatorView: indicatorView) .frame(width: chartRect.width, height: chartRect.height) - XAxisView(axisDataSource: axisDataSource) + XAxisView() .frame(height: xAxisRect.height) } else if model.valueType == .allNegative { - XAxisView(axisDataSource: axisDataSource, isShowBaselineOnly: model.xAxisLabelsPosition == .fixedBottom ? true : false) + XAxisView(isShowBaselineOnly: model.xAxisLabelsPosition == .fixedBottom ? true : false) .frame(height: xAxisRect.height) .zIndex(1) @@ -157,7 +157,7 @@ struct XYAxisChart: View { .frame(width: chartRect.width, height: chartRect.height) if model.xAxisLabelsPosition == .fixedBottom { - XAxisView(axisDataSource: axisDataSource, isShowLabelsOnly: true) + XAxisView(isShowLabelsOnly: true) .frame(height: xAxisLabelsRect.height) .zIndex(1) } @@ -166,13 +166,13 @@ struct XYAxisChart: View { GridLinesAndChartView(chartView: chartView, indicatorView: indicatorView) .frame(width: chartRect.width, height: chartRect.height) - XAxisView(axisDataSource: axisDataSource, isShowBaselineOnly: model.xAxisLabelsPosition == .fixedBottom ? true : false) + XAxisView(isShowBaselineOnly: model.xAxisLabelsPosition == .fixedBottom ? true : false) .frame(height: xAxisRect.height) .position(x: xAxisRect.size.width / 2, y: xAxisRect.origin.y + xAxisRect.size.height / 2) } if model.xAxisLabelsPosition == .fixedBottom { - XAxisView(axisDataSource: axisDataSource, isShowLabelsOnly: true) + XAxisView(isShowLabelsOnly: true) .frame(height: xAxisLabelsRect.height) .zIndex(1) } @@ -223,52 +223,13 @@ struct XYAxisChart: View { if model.chartType != .stock && model.categoryAxis.labelLayoutStyle == .allOrNothing && totalWidth > rect.size.width { axisDataSource.isEnoughSpaceToShowXAxisLables = false height = 0 + } else { + axisDataSource.isEnoughSpaceToShowXAxisLables = true } - + return height > 0 ? height + 3 : 0 } - func xAxisMaxHeight(_ rect: CGRect) -> CGFloat { - let labels = axisDataSource.xAxisLabels(model, rect: rect) - if labels.isEmpty || (model.categoryAxis.baseline.isHidden && model.categoryAxis.labels.isHidden) { - return 0 - } - - if !model.categoryAxis.baseline.isHidden && model.categoryAxis.labels.isHidden { - return max(0, model.categoryAxis.baseline.width) - } - - var height: CGFloat = 16 - var totalWidth: CGFloat = 0 - var prevXPos: CGFloat = -100000 - var prevLabelWidth: CGFloat = 0 - for label in labels { - let size: CGSize = label.title.isEmpty ? .zero : label.title.boundingBoxSize(with: model.categoryAxis.labels.fontSize) - // spacing btw baseline and labels are 3pt - height = max(height, size.height + model.categoryAxis.baseline.width + 3) - - // check if the gap btw two adjacent labels is greater than 4pt - if label.pos.x < prevXPos + prevLabelWidth / 2.0 + size.width / 2.0 + 4 { - totalWidth += rect.size.width - } - // min spacing btw labels are 4pt - if size.width > 0 { - totalWidth += size.width + 4 - prevXPos = label.pos.x - prevLabelWidth = size.width - } - } - totalWidth -= 4 - - // show nothing - if model.chartType != .stock && model.categoryAxis.labelLayoutStyle == .allOrNothing && totalWidth > rect.size.width { - axisDataSource.isEnoughSpaceToShowXAxisLables = false - height = 0 - } - - return height + model.categoryAxis.baseline.width + 3 - } - /** Minimum: 20px from left edge of content area Maximum: 35% of content area diff --git a/Sources/FioriCharts/Charts/StackedBarChart/StackedBarChart.swift b/Sources/FioriCharts/Charts/StackedBarChart/StackedBarChart.swift index 7f14e375a..e7e7f7cda 100644 --- a/Sources/FioriCharts/Charts/StackedBarChart/StackedBarChart.swift +++ b/Sources/FioriCharts/Charts/StackedBarChart/StackedBarChart.swift @@ -205,7 +205,7 @@ class StackedBarAxisDataSource: DefaultAxisDataSource { var startIndex = Int(startPosY / unitHeight).clamp(low: 0, high: maxDataCount - 1) var startOffset = unitHeight * CGFloat(startIndex) - startPosY - if abs(startOffset) >= clusterHeight { + if abs(startOffset) >= clusterHeight && startIndex < maxDataCount - 1 { startIndex += 1 startOffset = unitHeight * CGFloat(startIndex) - startPosY } diff --git a/Sources/FioriCharts/Charts/StackedColumnChart/StackedColumnChart.swift b/Sources/FioriCharts/Charts/StackedColumnChart/StackedColumnChart.swift index 662b8066f..14025e726 100644 --- a/Sources/FioriCharts/Charts/StackedColumnChart/StackedColumnChart.swift +++ b/Sources/FioriCharts/Charts/StackedColumnChart/StackedColumnChart.swift @@ -171,7 +171,7 @@ class StackedColumnAxisDataSource: DefaultAxisDataSource { var startIndex = Int(model.startPos.x / unitWidth).clamp(low: 0, high: maxDataCount - 1) var startOffset = unitWidth * CGFloat(startIndex) - model.startPos.x - if abs(startOffset) >= clusterWidth { + if abs(startOffset) >= clusterWidth && startIndex < maxDataCount - 1 { startIndex += 1 startOffset = unitWidth * CGFloat(startIndex) - model.startPos.x }