chore(Widget): Import LightChart fork using SPM
This commit is contained in:
parent
c8944146e6
commit
cd64668c08
|
@ -25,12 +25,6 @@
|
|||
2A1FE47C2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */; };
|
||||
2A1FE47E2938C11200784BF1 /* Collection+IsNotEmpty.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A1FE47D2938C11200784BF1 /* Collection+IsNotEmpty.swift */; };
|
||||
2A33062D2987DBFA001D4C51 /* FollowersCountHistory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A33062C2987DBFA001D4C51 /* FollowersCountHistory.swift */; };
|
||||
2A33063629880835001D4C51 /* Math.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A33062F29880834001D4C51 /* Math.swift */; };
|
||||
2A33063729880835001D4C51 /* DataRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A33063029880834001D4C51 /* DataRepresentable.swift */; };
|
||||
2A33063829880835001D4C51 /* LineChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A33063229880834001D4C51 /* LineChart.swift */; };
|
||||
2A33063929880835001D4C51 /* CurvedChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A33063329880834001D4C51 /* CurvedChart.swift */; };
|
||||
2A33063A29880835001D4C51 /* LightChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A33063429880834001D4C51 /* LightChart.swift */; };
|
||||
2A33063B29880835001D4C51 /* ChartType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A33063529880834001D4C51 /* ChartType.swift */; };
|
||||
2A33AB662982C4AF008A7FB1 /* FollowersCountWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A33AB652982C4AF008A7FB1 /* FollowersCountWidgetView.swift */; };
|
||||
2A3F6FE3292ECB5E002E6DA7 /* FollowedTagsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3F6FE2292ECB5E002E6DA7 /* FollowedTagsViewModel.swift */; };
|
||||
2A3F6FE5292F6E44002E6DA7 /* FollowedTagsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3F6FE4292F6E44002E6DA7 /* FollowedTagsTableViewCell.swift */; };
|
||||
|
@ -60,6 +54,7 @@
|
|||
2A9D0666298C05A800BF38CB /* LatestFollowersWidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9D0665298C05A800BF38CB /* LatestFollowersWidgetView.swift */; };
|
||||
2A9D066F298D0FD100BF38CB /* MastodonSDKDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 2A9D066E298D0FD100BF38CB /* MastodonSDKDynamic */; };
|
||||
2AB12E4629362F27006BC925 /* DataSourceFacade+Translate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AB12E4529362F27006BC925 /* DataSourceFacade+Translate.swift */; };
|
||||
2AB5011B2992322500346092 /* LightChart in Frameworks */ = {isa = PBXBuildFile; productRef = 2AB5011A2992322500346092 /* LightChart */; };
|
||||
2AE202AA297FE10B00F66E55 /* WidgetExtension.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 2A72812C297EA9D7004138C5 /* WidgetExtension.intentdefinition */; };
|
||||
2AE202AC297FE19100F66E55 /* FollowersCountIntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE202AB297FE19100F66E55 /* FollowersCountIntentHandler.swift */; };
|
||||
2AE202AD297FE1CD00F66E55 /* WidgetExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A72813E297EC762004138C5 /* WidgetExtension.swift */; };
|
||||
|
@ -618,12 +613,6 @@
|
|||
2A1FE47B2938BB2600784BF1 /* FollowedTagsViewModel+DiffableDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FollowedTagsViewModel+DiffableDataSource.swift"; sourceTree = "<group>"; };
|
||||
2A1FE47D2938C11200784BF1 /* Collection+IsNotEmpty.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Collection+IsNotEmpty.swift"; sourceTree = "<group>"; };
|
||||
2A33062C2987DBFA001D4C51 /* FollowersCountHistory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersCountHistory.swift; sourceTree = "<group>"; };
|
||||
2A33062F29880834001D4C51 /* Math.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Math.swift; sourceTree = "<group>"; };
|
||||
2A33063029880834001D4C51 /* DataRepresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataRepresentable.swift; sourceTree = "<group>"; };
|
||||
2A33063229880834001D4C51 /* LineChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineChart.swift; sourceTree = "<group>"; };
|
||||
2A33063329880834001D4C51 /* CurvedChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CurvedChart.swift; sourceTree = "<group>"; };
|
||||
2A33063429880834001D4C51 /* LightChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LightChart.swift; sourceTree = "<group>"; };
|
||||
2A33063529880834001D4C51 /* ChartType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartType.swift; sourceTree = "<group>"; };
|
||||
2A33625329759B4200481A90 /* OpenInActionExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = OpenInActionExtension.entitlements; sourceTree = "<group>"; };
|
||||
2A33AB652982C4AF008A7FB1 /* FollowersCountWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersCountWidgetView.swift; sourceTree = "<group>"; };
|
||||
2A3F6FE2292ECB5E002E6DA7 /* FollowedTagsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowedTagsViewModel.swift; sourceTree = "<group>"; };
|
||||
|
@ -1220,6 +1209,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2AB5011B2992322500346092 /* LightChart in Frameworks */,
|
||||
2A9D066F298D0FD100BF38CB /* MastodonSDKDynamic in Frameworks */,
|
||||
2A728124297EA9D7004138C5 /* SwiftUI.framework in Frameworks */,
|
||||
2A728122297EA9D7004138C5 /* WidgetKit.framework in Frameworks */,
|
||||
|
@ -1398,27 +1388,6 @@
|
|||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2A33062E29880834001D4C51 /* LightChart */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2A33063129880834001D4C51 /* Charts */,
|
||||
2A33062F29880834001D4C51 /* Math.swift */,
|
||||
2A33063029880834001D4C51 /* DataRepresentable.swift */,
|
||||
2A33063429880834001D4C51 /* LightChart.swift */,
|
||||
2A33063529880834001D4C51 /* ChartType.swift */,
|
||||
);
|
||||
path = LightChart;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2A33063129880834001D4C51 /* Charts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2A33063229880834001D4C51 /* LineChart.swift */,
|
||||
2A33063329880834001D4C51 /* CurvedChart.swift */,
|
||||
);
|
||||
path = Charts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2A506CF2292CD83B00059C37 /* FollowedTags */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1445,7 +1414,6 @@
|
|||
2A728125297EA9D7004138C5 /* WidgetExtension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
2A33062E29880834001D4C51 /* LightChart */,
|
||||
2A86A14329892700007F1062 /* Variants */,
|
||||
2AE202A9297FDDF500F66E55 /* WidgetExtension.entitlements */,
|
||||
2A72813E297EC762004138C5 /* WidgetExtension.swift */,
|
||||
|
@ -3034,6 +3002,7 @@
|
|||
name = WidgetExtension;
|
||||
packageProductDependencies = (
|
||||
2A9D066E298D0FD100BF38CB /* MastodonSDKDynamic */,
|
||||
2AB5011A2992322500346092 /* LightChart */,
|
||||
);
|
||||
productName = WidgetExtensionExtension;
|
||||
productReference = 2A728120297EA9D7004138C5 /* WidgetExtension.appex */;
|
||||
|
@ -3247,6 +3216,7 @@
|
|||
);
|
||||
mainGroup = DB427DC925BAA00100D1B89D;
|
||||
packageReferences = (
|
||||
2AB501192992322500346092 /* XCRemoteSwiftPackageReference "LightChart" */,
|
||||
);
|
||||
productRefGroup = DB427DD325BAA00100D1B89D /* Products */;
|
||||
projectDirPath = "";
|
||||
|
@ -3512,22 +3482,16 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2A33063729880835001D4C51 /* DataRepresentable.swift in Sources */,
|
||||
2A33062D2987DBFA001D4C51 /* FollowersCountHistory.swift in Sources */,
|
||||
2A33063829880835001D4C51 /* LineChart.swift in Sources */,
|
||||
2A9D0666298C05A800BF38CB /* LatestFollowersWidgetView.swift in Sources */,
|
||||
2A728130297EA9D8004138C5 /* WidgetExtension.intentdefinition in Sources */,
|
||||
2A86A14B2989326E007F1062 /* MultiFollowersCountWidgetView.swift in Sources */,
|
||||
2A72813F297EC762004138C5 /* WidgetExtension.swift in Sources */,
|
||||
2A33063A29880835001D4C51 /* LightChart.swift in Sources */,
|
||||
2A33063B29880835001D4C51 /* ChartType.swift in Sources */,
|
||||
2A33063629880835001D4C51 /* Math.swift in Sources */,
|
||||
2A86A14929892B3A007F1062 /* MultiFollowersCountWidget.swift in Sources */,
|
||||
2A33AB662982C4AF008A7FB1 /* FollowersCountWidgetView.swift in Sources */,
|
||||
2A9D0664298C048800BF38CB /* LatestFollowersWidget.swift in Sources */,
|
||||
2A728127297EA9D7004138C5 /* WidgetExtensionBundle.swift in Sources */,
|
||||
2A72812B297EA9D7004138C5 /* FollowersCountWidget.swift in Sources */,
|
||||
2A33063929880835001D4C51 /* CurvedChart.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -5330,6 +5294,17 @@
|
|||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
2AB501192992322500346092 /* XCRemoteSwiftPackageReference "LightChart" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/Bearologics/LightChart.git";
|
||||
requirement = {
|
||||
branch = master;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
2A90A156296EEE500026C155 /* MastodonSDKDynamic */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
|
@ -5339,6 +5314,11 @@
|
|||
isa = XCSwiftPackageProductDependency;
|
||||
productName = MastodonSDKDynamic;
|
||||
};
|
||||
2AB5011A2992322500346092 /* LightChart */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 2AB501192992322500346092 /* XCRemoteSwiftPackageReference "LightChart" */;
|
||||
productName = LightChart;
|
||||
};
|
||||
357FEEAE29523D470021C9DC /* MastodonSDKDynamic */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
productName = MastodonSDKDynamic;
|
||||
|
|
|
@ -73,6 +73,15 @@
|
|||
"version": "4.2.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "LightChart",
|
||||
"repositoryURL": "https://github.com/Bearologics/LightChart.git",
|
||||
"state": {
|
||||
"branch": "master",
|
||||
"revision": "a7e724e9ec3cdcaa2d0840b95780e66b870dbf1e",
|
||||
"version": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "MetaTextKit",
|
||||
"repositoryURL": "https://github.com/TwidereProject/MetaTextKit.git",
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
//
|
||||
// ChartType.swift
|
||||
//
|
||||
//
|
||||
// Created by Alexey Pichukov on 19.08.2020.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
public enum ChartType {
|
||||
case line
|
||||
case curved
|
||||
}
|
||||
|
||||
public enum ChartVisualType {
|
||||
case outline(color: Color, lineWidth: CGFloat)
|
||||
case filled(color: Color, lineWidth: CGFloat)
|
||||
case customFilled(color: Color, lineWidth: CGFloat, fillGradient: LinearGradient)
|
||||
}
|
||||
|
||||
public enum CurrentValueLineType {
|
||||
case none
|
||||
case line(color: Color, lineWidth: CGFloat)
|
||||
case dash(color: Color, lineWidth: CGFloat, dash: [CGFloat])
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
//
|
||||
// File.swift
|
||||
//
|
||||
//
|
||||
// Created by Alexey Pichukov on 20.08.2020.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
public struct CurvedChart: View {
|
||||
|
||||
private let data: [Double]
|
||||
private let frame: CGRect
|
||||
private let offset: Double
|
||||
private let type: ChartVisualType
|
||||
private let currentValueLineType: CurrentValueLineType
|
||||
private var points: [CGPoint] = []
|
||||
|
||||
/// Creates a new `CurvedChart`
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - data: A data set that should be presented on the chart
|
||||
/// - frame: A frame from the parent view
|
||||
/// - visualType: A type of chart, `.outline` by default
|
||||
/// - offset: An offset for the chart, a space below the chart in percentage (0 - 1)
|
||||
/// For example `offset: 0.2` means that the chart will occupy 80% of the upper
|
||||
/// part of the view
|
||||
/// - currentValueLineType: A type of current value line (`none` for no line on chart)
|
||||
public init(data: [Double],
|
||||
frame: CGRect,
|
||||
visualType: ChartVisualType = .outline(color: .red, lineWidth: 2),
|
||||
offset: Double = 0,
|
||||
currentValueLineType: CurrentValueLineType = .none) {
|
||||
self.data = data
|
||||
self.frame = frame
|
||||
self.type = visualType
|
||||
self.offset = offset
|
||||
self.currentValueLineType = currentValueLineType
|
||||
self.points = points(forData: data,
|
||||
frame: frame,
|
||||
offset: offset,
|
||||
lineWidth: lineWidth(visualType: visualType))
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
ZStack {
|
||||
chart
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.drawingGroup()
|
||||
line
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.drawingGroup()
|
||||
}
|
||||
}
|
||||
|
||||
private var chart: some View {
|
||||
switch type {
|
||||
case .outline(let color, let lineWidth):
|
||||
return AnyView(curvedPath(points: points)
|
||||
.stroke(color, style: StrokeStyle(lineWidth: lineWidth, lineJoin: .round)))
|
||||
case .filled(let color, let lineWidth):
|
||||
return AnyView(ZStack {
|
||||
curvedPathGradient(points: points)
|
||||
.fill(LinearGradient(
|
||||
gradient: .init(colors: [color.opacity(0.2), color.opacity(0.02)]),
|
||||
startPoint: .init(x: 0.5, y: 1),
|
||||
endPoint: .init(x: 0.5, y: 0)
|
||||
))
|
||||
curvedPath(points: points)
|
||||
.stroke(color, style: StrokeStyle(lineWidth: lineWidth, lineJoin: .round))
|
||||
})
|
||||
case .customFilled(let color, let lineWidth, let fillGradient):
|
||||
return AnyView(ZStack {
|
||||
curvedPathGradient(points: points)
|
||||
.fill(fillGradient)
|
||||
curvedPath(points: points)
|
||||
.stroke(color, style: StrokeStyle(lineWidth: lineWidth, lineJoin: .round))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private var line: some View {
|
||||
switch currentValueLineType {
|
||||
case .none:
|
||||
return AnyView(EmptyView())
|
||||
case .line(let color, let lineWidth):
|
||||
return AnyView(
|
||||
currentValueLinePath(points: points)
|
||||
.stroke(color, style: StrokeStyle(lineWidth: lineWidth))
|
||||
)
|
||||
case .dash(let color, let lineWidth, let dash):
|
||||
return AnyView(
|
||||
currentValueLinePath(points: points)
|
||||
.stroke(color, style: StrokeStyle(lineWidth: lineWidth, dash: dash))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: private functions
|
||||
|
||||
private func curvedPath(points: [CGPoint]) -> Path {
|
||||
func mid(_ point1: CGPoint, _ point2: CGPoint) -> CGPoint {
|
||||
return CGPoint(x: (point1.x + point2.x) / 2, y:(point1.y + point2.y) / 2)
|
||||
}
|
||||
|
||||
func control(_ point1: CGPoint, _ point2: CGPoint) -> CGPoint {
|
||||
var controlPoint = mid(point1, point2)
|
||||
let delta = abs(point2.y - controlPoint.y)
|
||||
|
||||
if point1.y < point2.y {
|
||||
controlPoint.y += delta
|
||||
} else if point1.y > point2.y {
|
||||
controlPoint.y -= delta
|
||||
}
|
||||
|
||||
return controlPoint
|
||||
}
|
||||
|
||||
var path = Path()
|
||||
guard points.count > 1 else {
|
||||
return path
|
||||
}
|
||||
|
||||
var startPoint = points[0]
|
||||
path.move(to: startPoint)
|
||||
|
||||
guard points.count > 2 else {
|
||||
path.addLine(to: points[1])
|
||||
return path
|
||||
}
|
||||
|
||||
for i in 1..<points.count {
|
||||
let currentPoint = points[i]
|
||||
let midPoint = mid(startPoint, currentPoint)
|
||||
|
||||
path.addQuadCurve(to: midPoint, control: control(midPoint, startPoint))
|
||||
path.addQuadCurve(to: currentPoint, control: control(midPoint, currentPoint))
|
||||
|
||||
startPoint = currentPoint
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
private func curvedPathGradient(points: [CGPoint]) -> Path {
|
||||
var path = curvedPath(points: points)
|
||||
guard let lastPoint = points.last else {
|
||||
return path
|
||||
}
|
||||
path.addLine(to: CGPoint(x: lastPoint.x, y: 0))
|
||||
path.addLine(to: CGPoint(x: 0, y: 0))
|
||||
path.addLine(to: CGPoint(x: 0, y: points[0].y))
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
private func currentValueLinePath(points: [CGPoint]) -> Path {
|
||||
var path = Path()
|
||||
guard let lastPoint = points.last else {
|
||||
return path
|
||||
}
|
||||
path.move(to: CGPoint(x: 0, y: lastPoint.y))
|
||||
path.addLine(to: lastPoint)
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
extension CurvedChart: DataRepresentable { }
|
|
@ -1,138 +0,0 @@
|
|||
//
|
||||
// LineChart.swift
|
||||
//
|
||||
//
|
||||
// Created by Alexey Pichukov on 19.08.2020.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
public struct LineChart: View {
|
||||
|
||||
private let data: [Double]
|
||||
private let frame: CGRect
|
||||
private let offset: Double
|
||||
private let type: ChartVisualType
|
||||
private let currentValueLineType: CurrentValueLineType
|
||||
private var points: [CGPoint] = []
|
||||
|
||||
/// Creates a new `LineChart`
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - data: A data set that should be presented on the chart
|
||||
/// - frame: A frame from the parent view
|
||||
/// - visualType: A type of chart, `.outline` by default
|
||||
/// - offset: An offset for the chart, a space below the chart in percentage (0 - 1)
|
||||
/// For example `offset: 0.2` means that the chart will occupy 80% of the upper
|
||||
/// part of the view
|
||||
/// - currentValueLineType: A type of current value line (`none` for no line on chart)
|
||||
public init(data: [Double],
|
||||
frame: CGRect,
|
||||
visualType: ChartVisualType = .outline(color: .red, lineWidth: 2),
|
||||
offset: Double = 0,
|
||||
currentValueLineType: CurrentValueLineType = .none) {
|
||||
self.data = data
|
||||
self.frame = frame
|
||||
self.type = visualType
|
||||
self.offset = offset
|
||||
self.currentValueLineType = currentValueLineType
|
||||
self.points = points(forData: data,
|
||||
frame: frame,
|
||||
offset: offset,
|
||||
lineWidth: lineWidth(visualType: visualType))
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
ZStack {
|
||||
chart
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.drawingGroup()
|
||||
line
|
||||
.rotationEffect(.degrees(180), anchor: .center)
|
||||
.rotation3DEffect(.degrees(180), axis: (x: 0, y: 1, z: 0))
|
||||
.drawingGroup()
|
||||
}
|
||||
}
|
||||
|
||||
private var chart: some View {
|
||||
switch type {
|
||||
case .outline(let color, let lineWidth):
|
||||
return AnyView(linePath(points: points)
|
||||
.stroke(color, style: StrokeStyle(lineWidth: lineWidth, lineJoin: .round)))
|
||||
case .filled(let color, let lineWidth):
|
||||
return AnyView(ZStack {
|
||||
linePathGradient(points: points)
|
||||
.fill(LinearGradient(
|
||||
gradient: .init(colors: [color.opacity(0.2), color.opacity(0.02)]),
|
||||
startPoint: .init(x: 0.5, y: 1),
|
||||
endPoint: .init(x: 0.5, y: 0)
|
||||
))
|
||||
linePath(points: points)
|
||||
.stroke(color, style: StrokeStyle(lineWidth: lineWidth, lineJoin: .round))
|
||||
})
|
||||
case .customFilled(let color, let lineWidth, let fillGradient):
|
||||
return AnyView(ZStack {
|
||||
linePathGradient(points: points)
|
||||
.fill(fillGradient)
|
||||
linePath(points: points)
|
||||
.stroke(color, style: StrokeStyle(lineWidth: lineWidth, lineJoin: .round))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private var line: some View {
|
||||
switch currentValueLineType {
|
||||
case .none:
|
||||
return AnyView(EmptyView())
|
||||
case .line(let color, let lineWidth):
|
||||
return AnyView(
|
||||
currentValueLinePath(points: points)
|
||||
.stroke(color, style: StrokeStyle(lineWidth: lineWidth))
|
||||
)
|
||||
case .dash(let color, let lineWidth, let dash):
|
||||
return AnyView(
|
||||
currentValueLinePath(points: points)
|
||||
.stroke(color, style: StrokeStyle(lineWidth: lineWidth, dash: dash))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: private functions
|
||||
|
||||
private func linePath(points: [CGPoint]) -> Path {
|
||||
var path = Path()
|
||||
guard points.count > 1 else {
|
||||
return path
|
||||
}
|
||||
path.move(to: points[0])
|
||||
for i in 1..<points.count {
|
||||
path.addLine(to: points[i])
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
private func linePathGradient(points: [CGPoint]) -> Path {
|
||||
var path = linePath(points: points)
|
||||
guard let lastPoint = points.last else {
|
||||
return path
|
||||
}
|
||||
path.addLine(to: CGPoint(x: lastPoint.x, y: 0))
|
||||
path.addLine(to: CGPoint(x: 0, y: 0))
|
||||
path.addLine(to: CGPoint(x: 0, y: points[0].y))
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
private func currentValueLinePath(points: [CGPoint]) -> Path {
|
||||
var path = Path()
|
||||
guard let lastPoint = points.last else {
|
||||
return path
|
||||
}
|
||||
path.move(to: CGPoint(x: 0, y: lastPoint.y))
|
||||
path.addLine(to: lastPoint)
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
extension LineChart: DataRepresentable { }
|
|
@ -1,43 +0,0 @@
|
|||
//
|
||||
// DataRepresentable.swift
|
||||
//
|
||||
//
|
||||
// Created by Alexey Pichukov on 19.08.2020.
|
||||
//
|
||||
// Modified by Marcus Kida for Mastodon on 30.01.2023.
|
||||
|
||||
import Foundation
|
||||
import CoreGraphics
|
||||
|
||||
protocol DataRepresentable {
|
||||
func points(forData data: [Double], frame: CGRect, offset: Double, lineWidth: CGFloat) -> [CGPoint]
|
||||
func lineWidth(visualType: ChartVisualType) -> CGFloat
|
||||
}
|
||||
|
||||
extension DataRepresentable {
|
||||
|
||||
func points(forData data: [Double], frame: CGRect, offset: Double, lineWidth: CGFloat) -> [CGPoint] {
|
||||
var vector = Math.stretchOut(Math.norm(data))
|
||||
if offset != 0 {
|
||||
vector = Math.stretchIn(vector, offset: offset)
|
||||
}
|
||||
var points: [CGPoint] = []
|
||||
for i in 0..<vector.count {
|
||||
let x = frame.size.width / CGFloat(vector.count - 1) * CGFloat(i)
|
||||
let y = (frame.size.height - lineWidth) * CGFloat(vector[i]) + lineWidth / 2
|
||||
points.append(CGPoint(x: x, y: y))
|
||||
}
|
||||
return points
|
||||
}
|
||||
|
||||
func lineWidth(visualType: ChartVisualType) -> CGFloat {
|
||||
switch visualType {
|
||||
case .outline(_, let lineWidth):
|
||||
return lineWidth
|
||||
case .filled(_, let lineWidth):
|
||||
return lineWidth
|
||||
case .customFilled(_, let lineWidth, _):
|
||||
return lineWidth
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
import SwiftUI
|
||||
|
||||
public struct LightChartView: View {
|
||||
|
||||
private let data: [Double]
|
||||
private let type: ChartType
|
||||
private let visualType: ChartVisualType
|
||||
private let offset: Double
|
||||
private let currentValueLineType: CurrentValueLineType
|
||||
|
||||
public init(data: [Double],
|
||||
type: ChartType = .line,
|
||||
visualType: ChartVisualType = .outline(color: .red, lineWidth: 2),
|
||||
offset: Double = 0,
|
||||
currentValueLineType: CurrentValueLineType = .none) {
|
||||
self.data = data
|
||||
self.type = type
|
||||
self.visualType = visualType
|
||||
self.offset = offset
|
||||
self.currentValueLineType = currentValueLineType
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
GeometryReader { reader in
|
||||
chart(withFrame: CGRect(x: 0,
|
||||
y: 0,
|
||||
width: reader.frame(in: .local).width ,
|
||||
height: reader.frame(in: .local).height))
|
||||
}
|
||||
}
|
||||
|
||||
private func chart(withFrame frame: CGRect) -> AnyView {
|
||||
switch type {
|
||||
case .line:
|
||||
return AnyView(
|
||||
LineChart(data: data,
|
||||
frame: frame,
|
||||
visualType: visualType,
|
||||
offset: offset,
|
||||
currentValueLineType: currentValueLineType)
|
||||
)
|
||||
case .curved:
|
||||
return AnyView(
|
||||
CurvedChart(data: data,
|
||||
frame: frame,
|
||||
visualType: visualType,
|
||||
offset: offset,
|
||||
currentValueLineType: currentValueLineType)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
//
|
||||
// Math.swift
|
||||
//
|
||||
//
|
||||
// Created by Alexey Pichukov on 19.08.2020.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreGraphics
|
||||
|
||||
struct Math {
|
||||
|
||||
static func norm(_ vector: [Double]) -> [Double] {
|
||||
let norm = sqrt(Double(vector.reduce(0) { $0 + $1 * $1 }))
|
||||
return norm == 0 ? vector : vector.map { $0 / norm }
|
||||
}
|
||||
|
||||
static func stretchOut(_ vector: [Double]) -> [Double] {
|
||||
guard let min = vector.min(),
|
||||
let rawMax = vector.max() else {
|
||||
return vector
|
||||
}
|
||||
let max = rawMax - min
|
||||
return vector.map { ($0 - min) / (max != 0 ? max : 1) }
|
||||
}
|
||||
|
||||
static func stretchIn(_ vector: [Double], offset: Double) -> [Double] {
|
||||
guard let max = vector.max() else {
|
||||
return vector
|
||||
}
|
||||
let newMax = max - offset
|
||||
return vector.map { $0 * newMax + offset }
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import SwiftUI
|
|||
import WidgetKit
|
||||
import MastodonAsset
|
||||
import MastodonLocalization
|
||||
import LightChart
|
||||
|
||||
struct FollowersCountWidgetView: View {
|
||||
private let followersHistory = FollowersCountHistory.shared
|
||||
|
|
Loading…
Reference in New Issue