Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
@@ -0,0 +1,204 @@
//
// PercentChartComponentController.swift
// GraphTest
//
// Created by Andrei Salavei on 4/14/19.
// Copyright © 2019 Andrei Salavei. All rights reserved.
//
import Foundation
#if os(macOS)
import Cocoa
#else
import UIKit
#endif
class PercentChartComponentController: GeneralChartComponentController {
let mainPecentChartRenderer: PecentChartRenderer
let horizontalScalesRenderer: HorizontalScalesRenderer
let verticalScalesRenderer: VerticalScalesRenderer
let verticalLineRenderer: VerticalLinesRenderer
let previewPercentChartRenderer: PecentChartRenderer
var percentageData: PecentChartRenderer.PercentageData = .blank
init(isZoomed: Bool,
mainPecentChartRenderer: PecentChartRenderer,
horizontalScalesRenderer: HorizontalScalesRenderer,
verticalScalesRenderer: VerticalScalesRenderer,
verticalLineRenderer: VerticalLinesRenderer,
previewPercentChartRenderer: PecentChartRenderer) {
self.mainPecentChartRenderer = mainPecentChartRenderer
self.horizontalScalesRenderer = horizontalScalesRenderer
self.verticalScalesRenderer = verticalScalesRenderer
self.verticalLineRenderer = verticalLineRenderer
self.previewPercentChartRenderer = previewPercentChartRenderer
super.init(isZoomed: isZoomed)
}
override func initialize(chartsCollection: ChartsCollection, initialDate: Date, totalHorizontalRange _: ClosedRange<CGFloat>, totalVerticalRange _: ClosedRange<CGFloat>) {
let components = chartsCollection.chartValues.map { PecentChartRenderer.PercentageData.Component(color: $0.color,
values: $0.values.map { CGFloat($0) }) }
self.percentageData = PecentChartRenderer.PercentageData(locations: chartsCollection.axisValues.map { CGFloat($0.timeIntervalSince1970) },
components: components)
let totalHorizontalRange = PecentChartRenderer.PercentageData.horizontalRange(data: self.percentageData) ?? BaseConstants.defaultRange
let totalVerticalRange = BaseConstants.defaultRange
super.initialize(chartsCollection: chartsCollection,
initialDate: initialDate,
totalHorizontalRange: totalHorizontalRange,
totalVerticalRange: totalVerticalRange)
mainPecentChartRenderer.percentageData = self.percentageData
previewPercentChartRenderer.percentageData = self.percentageData
let axisValues: [CGFloat] = [0, 25, 50, 75, 100]
let labels: [LinesChartLabel] = axisValues.map { value in
return LinesChartLabel(value: value / 100, text: BaseConstants.detailsNumberFormatter.string(from: NSNumber(value: Double(value))) ?? "")
}
verticalScalesRenderer.setup(verticalLimitsLabels: labels, animated: false)
setupMainChart(horizontalRange: initialHorizontalRange, animated: false)
setupMainChart(verticalRange: initialVerticalRange, animated: false)
previewPercentChartRenderer.setup(verticalRange: totalVerticalRange, animated: false)
previewPercentChartRenderer.setup(horizontalRange: totalHorizontalRange, animated: false)
updateHorizontalLimitLabels(animated: false)
}
override func willAppear(animated: Bool) {
previewPercentChartRenderer.setup(verticalRange: totalVerticalRange, animated: animated)
previewPercentChartRenderer.setup(horizontalRange: totalHorizontalRange, animated: animated)
setConponentsVisible(visible: true, animated: true)
setupMainChart(verticalRange: initialVerticalRange, animated: animated)
setupMainChart(horizontalRange: initialHorizontalRange, animated: animated)
updatePreviewRangeClosure?(currentChartHorizontalRangeFraction, animated)
super.willAppear(animated: animated)
}
override func chartRangeDidUpdated(_ updatedRange: ClosedRange<CGFloat>) {
super.chartRangeDidUpdated(updatedRange)
initialHorizontalRange = updatedRange
setupMainChart(horizontalRange: updatedRange, animated: false)
updateHorizontalLimitLabels(animated: true)
}
func updateHorizontalLimitLabels(animated: Bool) {
updateHorizontalLimitLabels(horizontalScalesRenderer: horizontalScalesRenderer,
horizontalRange: initialHorizontalRange,
scaleType: isZoomed ? .hour : .day,
forceUpdate: false,
animated: animated)
}
func prepareAppearanceAnimation(horizontalRnage: ClosedRange<CGFloat>) {
setupMainChart(horizontalRange: horizontalRnage, animated: false)
setConponentsVisible(visible: false, animated: false)
}
func setConponentsVisible(visible: Bool, animated: Bool) {
mainPecentChartRenderer.setVisible(visible, animated: animated)
horizontalScalesRenderer.setVisible(visible, animated: animated)
verticalScalesRenderer.setVisible(visible, animated: animated)
verticalLineRenderer.setVisible(visible, animated: animated)
previewPercentChartRenderer.setVisible(visible, animated: animated)
}
func setupMainChart(horizontalRange: ClosedRange<CGFloat>, animated: Bool) {
mainPecentChartRenderer.setup(horizontalRange: horizontalRange, animated: animated)
horizontalScalesRenderer.setup(horizontalRange: horizontalRange, animated: animated)
verticalScalesRenderer.setup(horizontalRange: horizontalRange, animated: animated)
verticalLineRenderer.setup(horizontalRange: horizontalRange, animated: animated)
}
func setupMainChart(verticalRange: ClosedRange<CGFloat>, animated: Bool) {
mainPecentChartRenderer.setup(verticalRange: verticalRange, animated: animated)
horizontalScalesRenderer.setup(verticalRange: verticalRange, animated: animated)
verticalScalesRenderer.setup(verticalRange: verticalRange, animated: animated)
verticalLineRenderer.setup(verticalRange: verticalRange, animated: animated)
}
public override func updateChartsVisibility(visibility: [Bool], animated: Bool) {
super.updateChartsVisibility(visibility: visibility, animated: animated)
for (index, isVisible) in visibility.enumerated() {
mainPecentChartRenderer.setComponentVisible(isVisible, at: index, animated: animated)
previewPercentChartRenderer.setComponentVisible(isVisible, at: index, animated: animated)
}
verticalScalesRenderer.setVisible(visibility.contains(true), animated: animated)
}
override func chartDetailsViewModel(closestDate: Date, pointIndex: Int, currency: GraphCurrency? = nil, rate: Double = 1.0) -> ChartDetailsViewModel {
let visibleValues = visibleDetailsChartValues
let total = visibleValues.map { $0.values[pointIndex] }.reduce(0, +)
let values: [ChartDetailsViewModel.Value] = chartsCollection.chartValues.enumerated().map { arg in
let (index, component) = arg
return ChartDetailsViewModel.Value(prefix: total > 0 ? PercentConstants.percentValueFormatter.string(from: component.values[pointIndex] / total * 100) : "0%",
title: component.name,
value: BaseConstants.detailsNumberFormatter.string(from: component.values[pointIndex]),
color: component.color,
visible: chartVisibility[index])
}
let dateString: String
if isZoomed {
dateString = BaseConstants.timeDateFormatter.string(from: closestDate)
} else {
dateString = BaseConstants.headerMediumRangeFormatter.string(from: closestDate)
}
let viewModel = ChartDetailsViewModel(title: dateString,
showArrow: total > 0 && self.isZoomable && !self.isZoomed,
showPrefixes: true,
isLoading: false,
values: values,
totalValue: nil,
tapAction: { [weak self] in
self?.hideDetailsView(animated: true)
self?.zoomInOnDateClosure?(closestDate) },
hideAction: { [weak self] in
self?.hideDetailsView(animated: true)
})
return viewModel
}
var currentlyVisiblePercentageData: PecentChartRenderer.PercentageData {
var currentPercentageData = percentageData
currentPercentageData.components = chartVisibility.enumerated().compactMap { $0.element ? currentPercentageData.components[$0.offset] : nil }
return currentPercentageData
}
override var currentMainRangeRenderer: BaseChartRenderer {
return mainPecentChartRenderer
}
override var currentPreviewRangeRenderer: BaseChartRenderer {
return previewPercentChartRenderer
}
override func showDetailsView(at chartPosition: CGFloat, detailsViewPosition: CGFloat, dataIndex: Int, date: Date, animated: Bool, feedback: Bool) {
super.showDetailsView(at: chartPosition, detailsViewPosition: detailsViewPosition, dataIndex: dataIndex, date: date, animated: animated, feedback: feedback)
verticalLineRenderer.values = [chartPosition]
verticalLineRenderer.isEnabled = true
}
override func hideDetailsView(animated: Bool) {
super.hideDetailsView(animated: animated)
verticalLineRenderer.values = []
verticalLineRenderer.isEnabled = false
}
override func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
super.apply(theme: theme, strings: strings, animated: animated)
horizontalScalesRenderer.labelsColor = theme.chartLabelsColor
verticalScalesRenderer.labelsColor = theme.chartLabelsColor
verticalScalesRenderer.axisXColor = theme.barChartStrongLinesColor
verticalScalesRenderer.horizontalLinesColor = theme.barChartStrongLinesColor
verticalLineRenderer.linesColor = theme.chartStrongLinesColor
}
}
@@ -0,0 +1,307 @@
//
// PercentPieChartController.swift
// GraphTest
//
// Created by Andrei Salavei on 4/7/19.
// Copyright © 2019 Andrei Salavei. All rights reserved.
//
import Foundation
#if os(macOS)
import Cocoa
#else
import UIKit
#endif
enum PercentConstants {
static let percentValueFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.positiveSuffix = "%"
return formatter
}()
}
private enum Constants {
static let zoomedRange = 7
}
public class PercentPieChartController: BaseChartController {
let percentController: PercentChartComponentController
let pieController: PieChartComponentController
let transitionRenderer: PercentPieAnimationRenderer
var initiallyZoomed = false
public convenience init(chartsCollection: ChartsCollection, initiallyZoomed: Bool) {
self.init(chartsCollection: chartsCollection)
self.initiallyZoomed = initiallyZoomed
}
override public init(chartsCollection: ChartsCollection) {
transitionRenderer = PercentPieAnimationRenderer()
percentController = PercentChartComponentController(isZoomed: false,
mainPecentChartRenderer: PecentChartRenderer(),
horizontalScalesRenderer: HorizontalScalesRenderer(),
verticalScalesRenderer: VerticalScalesRenderer(),
verticalLineRenderer: VerticalLinesRenderer(),
previewPercentChartRenderer: PecentChartRenderer())
pieController = PieChartComponentController(isZoomed: true,
pieChartRenderer: PieChartRenderer(),
previewBarChartRenderer: BarChartRenderer())
super.init(chartsCollection: chartsCollection)
[percentController, pieController].forEach { controller in
controller.chartFrame = { [unowned self] in self.chartFrame() }
controller.cartViewBounds = { [unowned self] in self.cartViewBounds() }
controller.zoomInOnDateClosure = { [unowned self] date in
self.didTapZoomIn(date: date, pointIndex: 0)
}
controller.setChartTitleClosure = { [unowned self] (title, animated) in
self.setChartTitleClosure?(title, animated)
}
controller.setDetailsViewPositionClosure = { [unowned self] (position) in
self.setDetailsViewPositionClosure?(position)
}
controller.setDetailsChartVisibleClosure = { [unowned self] (visible, animated) in
self.setDetailsChartVisibleClosure?(visible, animated)
}
controller.setDetailsViewModel = { [unowned self] (viewModel, animated, feedback) in
self.setDetailsViewModel?(viewModel, animated, feedback)
}
controller.updatePreviewRangeClosure = { [unowned self] (fraction, animated) in
self.chartRangeUpdatedClosure?(fraction, animated)
}
controller.chartRangePagingClosure = { [unowned self] (isEnabled, pageSize) in
self.setChartRangePagingEnabled(isEnabled: isEnabled, minimumSelectionSize: pageSize)
}
}
transitionRenderer.isEnabled = false
}
public override var mainChartRenderers: [ChartViewRenderer] {
return [percentController.mainPecentChartRenderer,
transitionRenderer,
percentController.horizontalScalesRenderer,
percentController.verticalScalesRenderer,
percentController.verticalLineRenderer,
pieController.pieChartRenderer,
// performanceRenderer
]
}
public override var navigationRenderers: [ChartViewRenderer] {
return [percentController.previewPercentChartRenderer,
pieController.previewBarChartRenderer]
}
public override func initializeChart() {
percentController.initialize(chartsCollection: initialChartsCollection,
initialDate: Date(),
totalHorizontalRange: BaseConstants.defaultRange,
totalVerticalRange: BaseConstants.defaultRange)
switchToChart(chartsCollection: percentController.chartsCollection, isZoomed: false, animated: false)
if let lastDate = initialChartsCollection.axisValues.last, self.initiallyZoomed {
TimeInterval.animationDurationMultipler = 0.00001
self.didTapZoomIn(date: lastDate, animated: false)
TimeInterval.animationDurationMultipler = 1.0
}
}
func switchToChart(chartsCollection: ChartsCollection, isZoomed: Bool, animated: Bool) {
if animated {
TimeInterval.setDefaultDuration(.expandAnimationDuration)
DispatchQueue.main.asyncAfter(deadline: .now() + .expandAnimationDuration) {
TimeInterval.setDefaultDuration(.osXDuration)
}
}
super.isZoomed = isZoomed
if isZoomed {
let toHorizontalRange = pieController.initialHorizontalRange
pieController.updateChartsVisibility(visibility: percentController.chartVisibility, animated: false)
pieController.pieChartRenderer.setup(horizontalRange: percentController.currentHorizontalMainChartRange, animated: false)
pieController.previewBarChartRenderer.setup(horizontalRange: percentController.currentPreviewHorizontalRange, animated: false)
pieController.pieChartRenderer.setVisible(false, animated: false)
pieController.previewBarChartRenderer.setVisible(true, animated: false)
pieController.willAppear(animated: animated)
percentController.willDisappear(animated: animated)
pieController.pieChartRenderer.drawPie = false
percentController.mainPecentChartRenderer.isEnabled = false
setupTransitionRenderer()
percentController.setupMainChart(horizontalRange: toHorizontalRange, animated: animated)
percentController.previewPercentChartRenderer.setup(horizontalRange: toHorizontalRange, animated: animated)
percentController.setConponentsVisible(visible: false, animated: animated)
transitionRenderer.animate(fromDataToPie: true, animated: animated) { [weak self] in
self?.pieController.pieChartRenderer.drawPie = true
self?.percentController.mainPecentChartRenderer.isEnabled = true
}
} else {
if !pieController.chartsCollection.isBlank {
let fromHorizontalRange = pieController.currentHorizontalMainChartRange
let toHorizontalRange = percentController.initialHorizontalRange
pieController.pieChartRenderer.setup(horizontalRange: toHorizontalRange, animated: animated)
pieController.previewBarChartRenderer.setup(horizontalRange: toHorizontalRange, animated: animated)
pieController.pieChartRenderer.setVisible(false, animated: animated)
pieController.previewBarChartRenderer.setVisible(false, animated: animated)
percentController.updateChartsVisibility(visibility: pieController.chartVisibility, animated: false)
percentController.setupMainChart(horizontalRange: fromHorizontalRange, animated: false)
percentController.previewPercentChartRenderer.setup(horizontalRange: fromHorizontalRange, animated: false)
percentController.setConponentsVisible(visible: false, animated: false)
}
percentController.willAppear(animated: animated)
pieController.willDisappear(animated: animated)
if animated {
pieController.pieChartRenderer.drawPie = false
percentController.mainPecentChartRenderer.isEnabled = false
setupTransitionRenderer()
transitionRenderer.animate(fromDataToPie: false, animated: true) {
self.pieController.pieChartRenderer.drawPie = true
self.percentController.mainPecentChartRenderer.isEnabled = true
}
}
}
self.setBackButtonVisibilityClosure?(isZoomed, animated)
}
func setupTransitionRenderer() {
transitionRenderer.setup(verticalRange: percentController.currentVerticalMainChartRange, animated: false)
transitionRenderer.setup(horizontalRange: percentController.currentHorizontalMainChartRange, animated: false)
transitionRenderer.visiblePieComponents = pieController.visiblePieDataWithCurrentPreviewRange
transitionRenderer.visiblePercentageData = percentController.currentlyVisiblePercentageData
}
public override func updateChartsVisibility(visibility: [Bool], animated: Bool) {
if isZoomed {
pieController.updateChartsVisibility(visibility: visibility, animated: animated)
} else {
percentController.updateChartsVisibility(visibility: visibility, animated: animated)
}
}
var visibleChartValues: [ChartsCollection.Chart] {
let visibility = isZoomed ? pieController.chartVisibility : percentController.chartVisibility
let collection = isZoomed ? pieController.chartsCollection : percentController.chartsCollection
let visibleCharts: [ChartsCollection.Chart] = visibility.enumerated().compactMap { args in
args.element ? collection.chartValues[args.offset] : nil
}
return visibleCharts
}
public override var actualChartVisibility: [Bool] {
return isZoomed ? pieController.chartVisibility : percentController.chartVisibility
}
public override var actualChartsCollection: ChartsCollection {
let collection = isZoomed ? pieController.chartsCollection : percentController.chartsCollection
if collection.isBlank {
return self.initialChartsCollection
}
return collection
}
public override func chartInteractionDidBegin(point: CGPoint, manual: Bool = true) {
if isZoomed {
pieController.chartInteractionDidBegin(point: point, manual: manual)
} else {
percentController.chartInteractionDidBegin(point: point, manual: manual)
}
}
public override func chartInteractionDidEnd() {
if isZoomed {
pieController.chartInteractionDidEnd()
} else {
percentController.chartInteractionDidEnd()
}
}
public override var drawChartVisibity: Bool {
return true
}
public override var currentChartHorizontalRangeFraction: ClosedRange<CGFloat> {
if isZoomed {
return pieController.currentChartHorizontalRangeFraction
} else {
return percentController.currentChartHorizontalRangeFraction
}
}
public override func cancelChartInteraction() {
if isZoomed {
return pieController.hideDetailsView(animated: true)
} else {
return percentController.hideDetailsView(animated: true)
}
}
func didTapZoomIn(date: Date, animated: Bool) {
guard !isZoomed, isZoomable else { return }
cancelChartInteraction()
let currentCollection = percentController.chartsCollection
let range: Int = Constants.zoomedRange
guard let (foundDate, index) = percentController.findClosestDateTo(dateToFind: date) else { return }
var lowIndex = max(0, index - range / 2)
var highIndex = min(currentCollection.axisValues.count - 1, index + range / 2)
if lowIndex == 0 {
highIndex = min(currentCollection.axisValues.count - 1, lowIndex + (range - 1))
} else if highIndex == currentCollection.axisValues.count - 1 {
lowIndex = max(0, highIndex - (range - 1))
}
let newValues = currentCollection.chartValues.map { chart in
return ChartsCollection.Chart(color: chart.color,
name: chart.name,
values: Array(chart.values[(lowIndex...highIndex)]))
}
let newCollection = ChartsCollection(axisValues: Array(currentCollection.axisValues[(lowIndex...highIndex)]),
chartValues: newValues)
let selectedRange = CGFloat(foundDate.timeIntervalSince1970 - .day)...CGFloat(foundDate.timeIntervalSince1970)
pieController.initialize(chartsCollection: newCollection, initialDate: date, totalHorizontalRange: 0...1, totalVerticalRange: 0...1)
pieController.initialHorizontalRange = selectedRange
switchToChart(chartsCollection: newCollection, isZoomed: true, animated: true)
}
public override func didTapZoomIn(date: Date, pointIndex: Int) {
self.didTapZoomIn(date: date, animated: true)
}
public override func didTapZoomOut() {
self.pieController.deselectSegment(completion: { [weak self] in
guard let self = self else { return }
self.switchToChart(chartsCollection: self.percentController.chartsCollection, isZoomed: false, animated: true)
})
}
public override func updateChartRange(_ rangeFraction: ClosedRange<CGFloat>, animated: Bool = true) {
if isZoomed {
return pieController.chartRangeFractionDidUpdated(rangeFraction)
} else {
return percentController.chartRangeFractionDidUpdated(rangeFraction)
}
}
public override func apply(theme: ChartTheme, strings: ChartStrings, animated: Bool) {
super.apply(theme: theme, strings: strings, animated: animated)
pieController.apply(theme: theme, strings: strings, animated: animated)
percentController.apply(theme: theme, strings: strings, animated: animated)
transitionRenderer.backgroundColor = theme.chartBackgroundColor
}
}
@@ -0,0 +1,198 @@
//
// PieChartComponentController.swift
// GraphTest
//
// Created by Andrei Salavei on 4/14/19.
// Copyright © 2019 Andrei Salavei. All rights reserved.
//
import Foundation
#if os(macOS)
import Cocoa
#else
import UIKit
#endif
class PieChartComponentController: GeneralChartComponentController {
let pieChartRenderer: PieChartRenderer
let previewBarChartRenderer: BarChartRenderer
var barWidth: CGFloat = 1
var chartBars: BarChartRenderer.BarsData = .blank
init(isZoomed: Bool,
pieChartRenderer: PieChartRenderer,
previewBarChartRenderer: BarChartRenderer) {
self.pieChartRenderer = pieChartRenderer
self.previewBarChartRenderer = previewBarChartRenderer
super.init(isZoomed: isZoomed)
}
override func initialize(chartsCollection: ChartsCollection, initialDate: Date, totalHorizontalRange _: ClosedRange<CGFloat>, totalVerticalRange _: ClosedRange<CGFloat>) {
let (width, chartBars, totalHorizontalRange, _) = BarChartRenderer.BarsData.initialComponents(chartsCollection: chartsCollection)
self.barWidth = width
self.chartBars = chartBars
super.initialize(chartsCollection: chartsCollection,
initialDate: initialDate,
totalHorizontalRange: totalHorizontalRange,
totalVerticalRange: BaseConstants.defaultRange)
self.previewBarChartRenderer.bars = chartBars
self.previewBarChartRenderer.fillToTop = true
pieChartRenderer.valuesFormatter = PercentConstants.percentValueFormatter
pieChartRenderer.setup(horizontalRange: initialHorizontalRange, animated: false)
previewBarChartRenderer.setup(verticalRange: initialVerticalRange, animated: false)
previewBarChartRenderer.setup(horizontalRange: initialHorizontalRange, animated: false)
pieChartRenderer.updatePercentageData(pieDataWithCurrentPreviewRange, animated: false)
pieChartRenderer.selectSegmentAt(at: nil, animated: false)
}
private var pieDataWithCurrentPreviewRange: [PieChartRenderer.PieComponent] {
let range = currentHorizontalMainChartRange
var pieComponents = chartsCollection.chartValues.map { PieChartRenderer.PieComponent(color: $0.color,
value: 0) }
guard var valueIndex = chartsCollection.axisValues.firstIndex(where: { CGFloat($0.timeIntervalSince1970) > (range.lowerBound + 1)}) else {
return pieComponents
}
var count = 0
while valueIndex < chartsCollection.axisValues.count, CGFloat(chartsCollection.axisValues[valueIndex].timeIntervalSince1970) <= range.upperBound {
count += 1
for pieIndex in pieComponents.indices {
pieComponents[pieIndex].value += CGFloat(chartsCollection.chartValues[pieIndex].values[valueIndex])
}
valueIndex += 1
}
return pieComponents
}
var visiblePieDataWithCurrentPreviewRange: [PieChartRenderer.PieComponent] {
let currentData = pieDataWithCurrentPreviewRange
return chartVisibility.enumerated().compactMap { $0.element ? currentData[$0.offset] : nil }
}
override func willAppear(animated: Bool) {
pieChartRenderer.setup(horizontalRange: initialHorizontalRange, animated: animated)
pieChartRenderer.setVisible(true, animated: animated)
previewBarChartRenderer.setup(verticalRange: totalVerticalRange, animated: animated)
previewBarChartRenderer.setup(horizontalRange: totalHorizontalRange, animated: animated)
previewBarChartRenderer.setVisible(true, animated: animated)
updatePreviewRangeClosure?(currentChartHorizontalRangeFraction, animated)
pieChartRenderer.updatePercentageData(pieDataWithCurrentPreviewRange, animated: false)
super.willAppear(animated: animated)
}
override func setupChartRangePaging() {
let valuesCount = chartsCollection.axisValues.count
guard valuesCount > 0 else { return }
chartRangePagingClosure?(true, 1.0 / CGFloat(valuesCount))
}
override func chartRangeDidUpdated(_ updatedRange: ClosedRange<CGFloat>) {
if isChartInteractionBegun {
chartInteractionDidBegin(point: lastChartInteractionPoint)
}
initialHorizontalRange = updatedRange
setupMainChart(horizontalRange: updatedRange, animated: true)
updateSelectedDataLabelIfNeeded()
}
func setupMainChart(horizontalRange: ClosedRange<CGFloat>, animated: Bool) {
pieChartRenderer.setup(horizontalRange: horizontalRange, animated: animated)
pieChartRenderer.updatePercentageData(pieDataWithCurrentPreviewRange, animated: animated)
}
public override func updateChartsVisibility(visibility: [Bool], animated: Bool) {
super.updateChartsVisibility(visibility: visibility, animated: animated)
for (index, isVisible) in visibility.enumerated() {
pieChartRenderer.setComponentVisible(isVisible, at: index, animated: animated)
previewBarChartRenderer.setComponentVisible(isVisible, at: index, animated: animated)
}
if let segment = pieChartRenderer.selectedSegment {
if !visibility[segment] {
pieChartRenderer.selectSegmentAt(at: nil, animated: true)
}
}
updateSelectedDataLabelIfNeeded()
}
func deselectSegment(completion: @escaping () -> Void) {
if pieChartRenderer.hasSelectedSegments {
hideDetailsView(animated: true)
pieChartRenderer.selectSegmentAt(at: nil, animated: true)
DispatchQueue.main.asyncAfter(deadline: .now() + .defaultDuration / 2) {
completion()
}
} else {
completion()
}
}
func updateSelectedDataLabelIfNeeded() {
if let segment = pieChartRenderer.selectedSegment {
self.setDetailsChartVisibleClosure?(true, true)
self.setDetailsViewModel?(chartDetailsViewModel(segmentInde: segment), false, false)
self.setDetailsViewPositionClosure?(chartFrame().width / 4)
} else {
self.setDetailsChartVisibleClosure?(false, true)
}
}
func chartDetailsViewModel(segmentInde: Int) -> ChartDetailsViewModel {
let pieItem = pieDataWithCurrentPreviewRange[segmentInde]
let title = chartsCollection.chartValues[segmentInde].name
let valueString = BaseConstants.detailsNumberFormatter.string(from: pieItem.value)
let viewModel = ChartDetailsViewModel(title: "",
showArrow: false,
showPrefixes: false,
isLoading: false,
values: [ChartDetailsViewModel.Value(prefix: nil,
title: title,
value: valueString,
color: pieItem.color,
visible: true)],
totalValue: nil,
tapAction: nil,
hideAction: { [weak self] in
self?.deselectSegment(completion: {})
})
return viewModel
}
override var currentMainRangeRenderer: BaseChartRenderer {
return pieChartRenderer
}
override var currentPreviewRangeRenderer: BaseChartRenderer {
return previewBarChartRenderer
}
public override func chartInteractionDidBegin(point: CGPoint, manual: Bool = true) {
if let segment = pieChartRenderer.selectedItemIndex(at: point) {
pieChartRenderer.selectSegmentAt(at: segment, animated: true)
updateSelectedDataLabelIfNeeded()
}
}
override func hideDetailsView(animated: Bool) {
pieChartRenderer.selectSegmentAt(at: nil, animated: animated)
updateSelectedDataLabelIfNeeded()
}
override func updateChartRangeTitle(animated: Bool) {
let fromDate = Date(timeIntervalSince1970: TimeInterval(currentHorizontalMainChartRange.lowerBound) + .day + 1)
let toDate = Date(timeIntervalSince1970: TimeInterval(currentHorizontalMainChartRange.upperBound))
if Calendar.utc.startOfDay(for: fromDate) == Calendar.utc.startOfDay(for: toDate) {
let stirng = BaseConstants.headerFullZoomedFormatter.string(from: fromDate)
self.setChartTitleClosure?(stirng, animated)
} else {
let stirng = "\(BaseConstants.headerMediumRangeFormatter.string(from: fromDate)) - \(BaseConstants.headerMediumRangeFormatter.string(from: toDate))"
self.setChartTitleClosure?(stirng, animated)
}
}
}