Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f426dbaada |
@@ -1,40 +1,98 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct AxisLabels<Content: View>: View {
|
||||
struct YAxisViewKey: ViewPreferenceKey { }
|
||||
struct ChartViewKey: ViewPreferenceKey { }
|
||||
|
||||
var axisLabelsData = AxisLabelsData()
|
||||
var axisLabelsStyle = AxisLabelsStyle()
|
||||
|
||||
@State private var yAxisWidth: CGFloat = 25
|
||||
@State private var chartWidth: CGFloat = 0
|
||||
@State private var chartHeight: CGFloat = 0
|
||||
|
||||
let content: () -> Content
|
||||
// font
|
||||
// foregroundColor
|
||||
|
||||
public init(@ViewBuilder content: @escaping () -> Content) {
|
||||
self.content = content
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
HStack {
|
||||
VStack {
|
||||
ForEach(Array(axisLabelsData.axisYLabels.reversed().enumerated()), id: \.element) { index, axisYData in
|
||||
Text(axisYData)
|
||||
if index != axisLabelsData.axisYLabels.count - 1 {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
var yAxis: some View {
|
||||
VStack(spacing: 0.0) {
|
||||
ForEach(Array(axisLabelsData.axisYLabels.reversed().enumerated()), id: \.element) { index, axisYData in
|
||||
Text(axisYData)
|
||||
.font(axisLabelsStyle.axisFont)
|
||||
.foregroundColor(axisLabelsStyle.axisFontColor)
|
||||
.frame(height: getYHeight(index: index,
|
||||
chartHeight: chartHeight,
|
||||
count: axisLabelsData.axisYLabels.count),
|
||||
alignment: getYAlignment(index: index, count: axisLabelsData.axisYLabels.count))
|
||||
}
|
||||
.padding([.trailing], 8.0)
|
||||
.padding([.bottom], axisLabelsData.axisXLabels.count > 0 ? 28.0 : 0)
|
||||
.frame(maxHeight: .infinity)
|
||||
VStack {
|
||||
self.content()
|
||||
HStack {
|
||||
ForEach(Array(axisLabelsData.axisXLabels.enumerated()), id: \.element) { index, axisXData in
|
||||
Text(axisXData)
|
||||
if index != axisLabelsData.axisXLabels.count - 1 {
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding([.top, .bottom], 10.0)
|
||||
}
|
||||
.padding([.leading, .trailing], 4.0)
|
||||
.background(ViewGeometry<YAxisViewKey>())
|
||||
.onPreferenceChange(YAxisViewKey.self) { value in
|
||||
yAxisWidth = value.first?.size.width ?? 0.0
|
||||
}
|
||||
}
|
||||
|
||||
func xAxis(chartWidth: CGFloat) -> some View {
|
||||
HStack(spacing: 0.0) {
|
||||
ForEach(Array(axisLabelsData.axisXLabels.enumerated()), id: \.element) { index, axisXData in
|
||||
Text(axisXData)
|
||||
.font(axisLabelsStyle.axisFont)
|
||||
.foregroundColor(axisLabelsStyle.axisFontColor)
|
||||
.frame(width: chartWidth / CGFloat(axisLabelsData.axisXLabels.count - 1))
|
||||
}
|
||||
}
|
||||
.frame(height: 24.0, alignment: .top)
|
||||
}
|
||||
|
||||
var chart: some View {
|
||||
self.content()
|
||||
.background(ViewGeometry<ChartViewKey>())
|
||||
.onPreferenceChange(ChartViewKey.self) { value in
|
||||
chartWidth = value.first?.size.width ?? 0.0
|
||||
chartHeight = value.first?.size.height ?? 0.0
|
||||
}
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
VStack(spacing: 0.0) {
|
||||
HStack {
|
||||
if axisLabelsStyle.axisLabelsYPosition == .leading {
|
||||
yAxis
|
||||
} else {
|
||||
Spacer(minLength: yAxisWidth)
|
||||
}
|
||||
chart
|
||||
if axisLabelsStyle.axisLabelsYPosition == .leading {
|
||||
Spacer(minLength: yAxisWidth)
|
||||
} else {
|
||||
yAxis
|
||||
}
|
||||
}
|
||||
xAxis(chartWidth: chartWidth)
|
||||
}
|
||||
}
|
||||
|
||||
private func getYHeight(index: Int, chartHeight: CGFloat, count: Int) -> CGFloat {
|
||||
if index == 0 || index == count - 1 {
|
||||
return chartHeight / (CGFloat(count - 1) * 2) + 10
|
||||
}
|
||||
|
||||
return chartHeight / CGFloat(count - 1)
|
||||
}
|
||||
|
||||
private func getYAlignment(index: Int, count: Int) -> Alignment {
|
||||
if index == 0 {
|
||||
return .top
|
||||
}
|
||||
|
||||
if index == count - 1 {
|
||||
return .bottom
|
||||
}
|
||||
|
||||
return .center
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import SwiftUI
|
||||
|
||||
extension AxisLabels {
|
||||
public func setAxisYLabels(_ labels: [String]) -> AxisLabels {
|
||||
public func setAxisYLabels(_ labels: [String],
|
||||
position: AxisLabelsYPosition = .leading) -> AxisLabels {
|
||||
self.axisLabelsData.axisYLabels = labels
|
||||
self.axisLabelsStyle.axisLabelsYPosition = position
|
||||
return self
|
||||
}
|
||||
|
||||
@@ -10,4 +12,46 @@ extension AxisLabels {
|
||||
self.axisLabelsData.axisXLabels = labels
|
||||
return self
|
||||
}
|
||||
|
||||
public func setAxisYLabels(_ labels: [(Double, String)],
|
||||
range: ClosedRange<Int>,
|
||||
position: AxisLabelsYPosition = .leading) -> AxisLabels {
|
||||
let overreach = range.overreach + 1
|
||||
var labelArray = [String](repeating: "", count: overreach)
|
||||
labels.forEach {
|
||||
let index = Int($0.0) - range.lowerBound
|
||||
if labelArray[safe: index] != nil {
|
||||
labelArray[index] = $0.1
|
||||
}
|
||||
}
|
||||
|
||||
self.axisLabelsData.axisYLabels = labelArray
|
||||
self.axisLabelsStyle.axisLabelsYPosition = position
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
public func setAxisXLabels(_ labels: [(Double, String)], range: ClosedRange<Int>) -> AxisLabels {
|
||||
let overreach = range.overreach + 1
|
||||
var labelArray = [String](repeating: "", count: overreach)
|
||||
labels.forEach {
|
||||
let index = Int($0.0) - range.lowerBound
|
||||
if labelArray[safe: index] != nil {
|
||||
labelArray[index] = $0.1
|
||||
}
|
||||
}
|
||||
|
||||
self.axisLabelsData.axisXLabels = labelArray
|
||||
return self
|
||||
}
|
||||
|
||||
public func setColor(_ color: Color) -> AxisLabels {
|
||||
self.axisLabelsStyle.axisFontColor = color
|
||||
return self
|
||||
}
|
||||
|
||||
public func setFont(_ font: Font) -> AxisLabels {
|
||||
self.axisLabelsStyle.axisFont = font
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import Foundation
|
||||
|
||||
public enum AxisLabelsYPosition {
|
||||
case leading
|
||||
case trailing
|
||||
}
|
||||
|
||||
public enum AxisLabelsXPosition {
|
||||
case top
|
||||
case bottom
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import SwiftUI
|
||||
|
||||
public final class AxisLabelsStyle: ObservableObject {
|
||||
@Published public var axisFont: Font = .callout
|
||||
@Published public var axisFontColor: Color = .primary
|
||||
@Published var axisLabelsYPosition: AxisLabelsYPosition = .leading
|
||||
@Published var axisLabelsXPosition: AxisLabelsXPosition = .bottom
|
||||
public init() {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
@@ -41,19 +41,11 @@ public class ChartData: ObservableObject {
|
||||
}
|
||||
|
||||
var normalisedYRange: Double {
|
||||
if let _ = rangeY {
|
||||
return 1
|
||||
}
|
||||
|
||||
return (normalisedPoints.max() ?? 0.0) - (normalisedPoints.min() ?? 0.0)
|
||||
return rangeY == nil ? (normalisedPoints.max() ?? 0.0) - (normalisedPoints.min() ?? 0.0) : 1
|
||||
}
|
||||
|
||||
var normalisedXRange: Double {
|
||||
if let _ = rangeX {
|
||||
return 1
|
||||
}
|
||||
|
||||
return (normalisedValues.max() ?? 0.0) - (normalisedValues.min() ?? 0.0)
|
||||
return rangeX == nil ? (normalisedValues.max() ?? 0.0) - (normalisedValues.min() ?? 0.0) : 1
|
||||
}
|
||||
|
||||
var isInNegativeDomain: Bool {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct ViewGeometry<T>: View where T: PreferenceKey {
|
||||
public var body: some View {
|
||||
GeometryReader { geometry in
|
||||
Color.clear
|
||||
.preference(key: T.self, value: [ViewSizeData(size: geometry.size)] as! T.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import SwiftUI
|
||||
|
||||
public protocol ViewPreferenceKey: PreferenceKey {
|
||||
typealias Value = [ViewSizeData]
|
||||
}
|
||||
|
||||
public extension ViewPreferenceKey {
|
||||
static var defaultValue: [ViewSizeData] {
|
||||
[]
|
||||
}
|
||||
|
||||
static func reduce(value: inout [ViewSizeData], nextValue: () -> [ViewSizeData]) {
|
||||
value.append(contentsOf: nextValue())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct ViewSizeData: Identifiable, Equatable, Hashable {
|
||||
public let id: UUID = UUID()
|
||||
public let size: CGSize
|
||||
|
||||
public static func == (lhs: Self, rhs: Self) -> Bool {
|
||||
return lhs.id == rhs.id
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
||||
@@ -17,3 +17,10 @@ extension Array where Element == ColorGradient {
|
||||
return self[index]
|
||||
}
|
||||
}
|
||||
|
||||
extension Collection {
|
||||
/// Returns the element at the specified index if it is within bounds, otherwise nil.
|
||||
subscript (safe index: Index) -> Element? {
|
||||
return indices.contains(index) ? self[index] : nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,15 +150,17 @@ extension Path {
|
||||
|
||||
static func drawChartMarkers(data: [(Double, Double)], in rect: CGRect) -> Path {
|
||||
var path = Path()
|
||||
if data.count < 1 {
|
||||
let filteredData = data.filter { $0.1 <= 1 && $0.1 >= 0 }
|
||||
|
||||
if filteredData.count < 1 {
|
||||
return path
|
||||
}
|
||||
|
||||
let convertedXValues = data.map { CGFloat($0.0) * rect.width }
|
||||
let convertedYPoints = data.map { CGFloat($0.1) * rect.height }
|
||||
let convertedXValues = filteredData.map { CGFloat($0.0) * rect.width }
|
||||
let convertedYPoints = filteredData.map { CGFloat($0.1) * rect.height }
|
||||
|
||||
let markerSize = CGSize(width: 8, height: 8)
|
||||
for pointIndex in 0..<data.count {
|
||||
for pointIndex in 0..<filteredData.count {
|
||||
path.addRoundedRect(in: CGRect(origin: CGPoint(x: convertedXValues[pointIndex] - markerSize.width / 2,
|
||||
y: convertedYPoints[pointIndex] - markerSize.height / 2),
|
||||
size: markerSize),
|
||||
@@ -199,7 +201,7 @@ extension Path {
|
||||
let convertedXValues = data.map { CGFloat($0.0) * rect.width }
|
||||
let convertedYPoints = data.map { CGFloat($0.1) * rect.height }
|
||||
|
||||
path.move(to: .zero)
|
||||
path.move(to: CGPoint(x: convertedXValues[0], y: 0))
|
||||
var point1 = CGPoint(x: convertedXValues[0], y: convertedYPoints[0])
|
||||
path.addLine(to: point1)
|
||||
for pointIndex in 1..<data.count {
|
||||
|
||||
@@ -8,4 +8,10 @@ extension View {
|
||||
public func chartStyle(_ style: ChartStyle) -> some View {
|
||||
self.environmentObject(style)
|
||||
}
|
||||
|
||||
public func toStandardCoordinateSystem() -> some View {
|
||||
self
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,11 @@ struct ChartGridShape_Previews: PreviewProvider {
|
||||
Group {
|
||||
ChartGridShape(numberOfHorizontalLines: 5, numberOfVerticalLines: 0)
|
||||
.stroke()
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.toStandardCoordinateSystem()
|
||||
|
||||
ChartGridShape(numberOfHorizontalLines: 4, numberOfVerticalLines: 4)
|
||||
.stroke()
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.toStandardCoordinateSystem()
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
@@ -6,13 +6,14 @@ extension LineChart {
|
||||
return self
|
||||
}
|
||||
|
||||
public func showBackground(_ show: Bool) -> LineChart {
|
||||
self.chartProperties.showBackground = show
|
||||
public func setBackground(colorGradient: ColorGradient) -> LineChart {
|
||||
self.chartProperties.backgroundGradient = colorGradient
|
||||
return self
|
||||
}
|
||||
|
||||
public func showChartMarks(_ show: Bool) -> LineChart {
|
||||
public func showChartMarks(_ show: Bool, with color: ColorGradient? = nil) -> LineChart {
|
||||
self.chartProperties.showChartMarks = show
|
||||
self.chartProperties.customChartMarksColors = color
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import SwiftUI
|
||||
|
||||
/// A single line of data, a view in a `LineChart`
|
||||
public struct Line: View {
|
||||
@EnvironmentObject var chartValue: ChartValue
|
||||
@ObservedObject var chartData: ChartData
|
||||
@ObservedObject var chartProperties: LineChartProperties
|
||||
|
||||
@@ -29,23 +28,22 @@ public struct Line: View {
|
||||
public var body: some View {
|
||||
GeometryReader { geometry in
|
||||
ZStack {
|
||||
if self.didCellAppear && self.chartProperties.showBackground {
|
||||
if self.didCellAppear, let backgroundColor = chartProperties.backgroundGradient {
|
||||
LineBackgroundShapeView(chartData: chartData,
|
||||
geometry: geometry,
|
||||
style: style)
|
||||
backgroundColor: backgroundColor)
|
||||
}
|
||||
LineShapeView(chartData: chartData,
|
||||
chartProperties: chartProperties,
|
||||
geometry: geometry,
|
||||
style: style,
|
||||
trimTo: didCellAppear ? 1.0 : 0.0)
|
||||
.animation(.easeIn)
|
||||
.animation(Animation.easeIn(duration: 0.75))
|
||||
if self.showIndicator {
|
||||
IndicatorPoint()
|
||||
.position(self.getClosestPointOnPath(geometry: geometry,
|
||||
touchLocation: self.touchLocation))
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.toStandardCoordinateSystem()
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
@@ -54,20 +52,17 @@ public struct Line: View {
|
||||
.onDisappear() {
|
||||
didCellAppear = false
|
||||
}
|
||||
|
||||
.gesture(DragGesture()
|
||||
.onChanged({ value in
|
||||
self.touchLocation = value.location
|
||||
self.showIndicator = true
|
||||
self.getClosestDataPoint(geometry: geometry, touchLocation: value.location)
|
||||
self.chartValue.interactionInProgress = true
|
||||
})
|
||||
.onEnded({ value in
|
||||
self.touchLocation = .zero
|
||||
self.showIndicator = false
|
||||
self.chartValue.interactionInProgress = false
|
||||
})
|
||||
)
|
||||
// .gesture(DragGesture()
|
||||
// .onChanged({ value in
|
||||
// self.touchLocation = value.location
|
||||
// self.showIndicator = true
|
||||
// self.getClosestDataPoint(geometry: geometry, touchLocation: value.location)
|
||||
// })
|
||||
// .onEnded({ value in
|
||||
// self.touchLocation = .zero
|
||||
// self.showIndicator = false
|
||||
// })
|
||||
// )
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,7 +89,7 @@ extension Line {
|
||||
let geometryWidth = geometry.frame(in: .local).width
|
||||
let index = Int(round((touchLocation.x / geometryWidth) * CGFloat(chartData.points.count - 1)))
|
||||
if (index >= 0 && index < self.chartData.data.count){
|
||||
self.chartValue.currentValue = self.chartData.points[index]
|
||||
// self.chartValue.currentValue = self.chartData.points[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,12 @@ struct LineBackgroundShape_Previews: PreviewProvider {
|
||||
GeometryReader { geometry in
|
||||
LineBackgroundShape(data: [(0, -0.5), (0.25, 0.8), (0.5,-0.6), (0.75,0.6), (1, 1)])
|
||||
.fill(Color.red)
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.toStandardCoordinateSystem()
|
||||
}
|
||||
GeometryReader { geometry in
|
||||
LineBackgroundShape(data: [(0, 0), (0.25, 0.5), (0.5,0.8), (0.75, 0.6), (1, 1)])
|
||||
.fill(Color.blue)
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.toStandardCoordinateSystem()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,14 @@ import SwiftUI
|
||||
struct LineBackgroundShapeView: View {
|
||||
var chartData: ChartData
|
||||
var geometry: GeometryProxy
|
||||
var style: ChartStyle
|
||||
var backgroundColor: ColorGradient
|
||||
|
||||
var body: some View {
|
||||
LineBackgroundShape(data: chartData.normalisedData)
|
||||
.fill(LinearGradient(gradient: Gradient(colors: [style.backgroundColor.startColor,
|
||||
style.foregroundColor.first?.startColor ?? .white]),
|
||||
.fill(LinearGradient(gradient: Gradient(colors: [backgroundColor.startColor,
|
||||
backgroundColor.endColor]),
|
||||
startPoint: .bottom,
|
||||
endPoint: .top))
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.toStandardCoordinateSystem()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,13 +20,11 @@ struct LineShape_Previews: PreviewProvider {
|
||||
Group {
|
||||
LineShape(data: [(0, 0), (0.25, 0.5), (0.5,0.8), (0.75, 0.6), (1, 1)])
|
||||
.stroke()
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.toStandardCoordinateSystem()
|
||||
|
||||
LineShape(data: [(0, -0.5), (0.25, 0.8), (0.5,-0.6), (0.75,0.6), (1, 1)], lineStyle: .straight)
|
||||
.stroke()
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.toStandardCoordinateSystem()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,16 @@ struct LineShapeView: View, Animatable {
|
||||
set { trimTo = Double(newValue) }
|
||||
}
|
||||
|
||||
var chartMarkColor: LinearGradient {
|
||||
if let customColor = chartProperties.customChartMarksColors {
|
||||
return customColor.linearGradient(from: .leading, to: .trailing)
|
||||
}
|
||||
|
||||
return LinearGradient(gradient: style.foregroundColor.first?.gradient ?? ColorGradient.orangeBright.gradient,
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
LineShape(data: chartData.normalisedData, lineStyle: chartProperties.lineStyle)
|
||||
@@ -21,19 +31,15 @@ struct LineShapeView: View, Animatable {
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing),
|
||||
style: StrokeStyle(lineWidth: chartProperties.lineWidth, lineJoin: .round))
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.toStandardCoordinateSystem()
|
||||
.clipped()
|
||||
if chartProperties.showChartMarks {
|
||||
MarkerShape(data: chartData.normalisedData)
|
||||
.trim(from: 0, to: CGFloat(trimTo))
|
||||
.fill(.white,
|
||||
strokeBorder: LinearGradient(gradient: style.foregroundColor.first?.gradient ?? ColorGradient.orangeBright.gradient,
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing),
|
||||
strokeBorder: chartMarkColor,
|
||||
lineWidth: chartProperties.lineWidth)
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.toStandardCoordinateSystem()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,13 +13,11 @@ struct MarkerShape_Previews: PreviewProvider {
|
||||
Group {
|
||||
MarkerShape(data: [(0, 0), (0.25, 0.5), (0.5,0.8), (0.75, 0.6), (1, 1)])
|
||||
.stroke()
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.toStandardCoordinateSystem()
|
||||
|
||||
MarkerShape(data: [(0, -0.5), (0.25, 0.8), (0.5,-0.6), (0.75,0.6), (1, 1)])
|
||||
.stroke()
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.toStandardCoordinateSystem()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,9 @@ import SwiftUI
|
||||
|
||||
public class LineChartProperties: ObservableObject {
|
||||
@Published var lineWidth: CGFloat = 2.0
|
||||
@Published var showBackground: Bool = false
|
||||
@Published var backgroundGradient: ColorGradient?
|
||||
@Published var showChartMarks: Bool = true
|
||||
@Published var customChartMarksColors: ColorGradient?
|
||||
@Published var lineStyle: LineStyle = .curved
|
||||
|
||||
public init() {
|
||||
|
||||
Reference in New Issue
Block a user