Skip to content

Commit

Permalink
v1.1: Various updates for MacOS native support.
Browse files Browse the repository at this point in the history
  • Loading branch information
DominikButz committed Nov 30, 2022
1 parent b0ce4d1 commit 6f2e3b5
Show file tree
Hide file tree
Showing 26 changed files with 594 additions and 171 deletions.
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import PackageDescription
let package = Package(
name: "SwiftUIGraphs",
platforms: [
.iOS(.v14)
.iOS(.v14),
.macOS(.v11)
], products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# SwiftUIGraphs (v1.0.3)
# SwiftUIGraphs (v1.1)


SwiftUIGraphs is a simple Swift package for iOS and iPadOS 14.0 and later. It features a line chart, bar chart and pie chart for data visualization and has many customization options.
SwiftUIGraphs is a Swift package for iOS, iPadOS (14.0 and later) as well as MacOS (11.0 and later). It features a line chart, bar chart and pie chart for data visualization and has many customization options.


## Feature Overview


**NEW**: from v1.1 SwiftUIGraphs supports MacOS (from v11) natively - in the included example project , simply select the macOS target and try it out!
* **NEW**: from v1.0 DYLineChartView and DYBarChartView support muliple data series by default (multi-line charts and stacked bar charts).
* Create an interactive line chart with a moving data selector point. The package also allows setting separate data point colors and line section colors.
* Create an area chart by adding a gradient area below the line chart.
Expand Down Expand Up @@ -513,6 +513,9 @@ struct RingChartAndDetailPieChartExample: View {

## Change log

#### [Version 1.1](https://github.com/DominikButz/SwiftUIGraphs/releases/tag/1.1)
Various updates for MacOS native support.

#### [Version 1.0.3](https://github.com/DominikButz/SwiftUIGraphs/releases/tag/1.0.3)
Minor update: Renaming selectorView parameter in DYLineView to selectorPointView.

Expand Down
72 changes: 67 additions & 5 deletions Sources/SwiftUIGraphs/Model/Extensions/Color+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ import SwiftUI

public extension Color {

static var defaultPlotAreaBackgroundColor: Color {
#if os(iOS)
return Color(UIColor.systemBackground)
#else
return Color(NSColor.controlBackgroundColor)
#endif
}

static func random()->Color {
let red = drand48()
let green = drand48()
Expand All @@ -26,13 +34,11 @@ public extension Color {
static func shadesOf(darker: Bool = true, color: Color, number: Int)->[Color] {
// let baseColor = color
var shades:[Color] = [color]
let step: CGFloat = 1.0 / CGFloat(number) * 100

#if os(iOS)
var uiColor = UIColor(color)

// get darker shades

let step: CGFloat = 1.0 / CGFloat(number) * 100

for _ in 0..<number - 1 {
if darker {
uiColor = uiColor.darker(by: step)
Expand All @@ -41,12 +47,29 @@ public extension Color {
}
shades.append(Color(uiColor))
}

#elseif os(macOS)

var nsColor = NSColor(color)
for _ in 0..<number - 1 {
if darker {
nsColor = nsColor.darker(by: step)
} else {
nsColor = nsColor.lighter(by: step)
}
shades.append(Color(nsColor))
}

#endif


return shades
}
}


// https://stackoverflow.com/questions/38435308/get-lighter-and-darker-color-variations-for-a-given-uicolor by Oscar
#if os(iOS)
public extension UIColor {

func lighter(by percentage: CGFloat = 30.0) -> UIColor {
Expand All @@ -56,6 +79,7 @@ public extension UIColor {
func darker(by percentage: CGFloat = 30.0) -> UIColor {
return self.adjustBrightness(by: -abs(percentage))
}


func adjustBrightness(by percentage: CGFloat = 30.0) -> UIColor {

Expand All @@ -65,7 +89,6 @@ public extension UIColor {
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
var alpha: CGFloat = 0.0

if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
let newRed = (red + ((ratio < 0) ? red * ratio : (1 - red) * ratio)).clamped(to: 0.0 ... 1.0)
let newGreen = (green + ((ratio < 0) ? green * ratio : (1 - green) * ratio)).clamped(to: 0.0 ... 1.0)
Expand All @@ -74,8 +97,47 @@ public extension UIColor {
}
return self
}

}

#endif


#if os(macOS)
public extension NSColor {

func lighter(by percentage: CGFloat = 30.0) -> NSColor {
return self.adjustBrightness(by: abs(percentage))
}

func darker(by percentage: CGFloat = 30.0) -> NSColor {
return self.adjustBrightness(by: -abs(percentage))
}


func adjustBrightness(by percentage: CGFloat = 30.0) -> NSColor {

let ratio = percentage/100

var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
var alpha: CGFloat = 0.0

self.usingColorSpace(NSColorSpace.deviceRGB)!.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
let newRed = (red + ((ratio < 0) ? red * ratio : (1 - red) * ratio)).clamped(to: 0.0 ... 1.0)
let newGreen = (green + ((ratio < 0) ? green * ratio : (1 - green) * ratio)).clamped(to: 0.0 ... 1.0)
let newBlue = (blue + ((ratio < 0) ? blue * ratio : (1 - blue) * ratio)).clamped(to: 0.0 ... 1.0)
return NSColor(red: newRed, green: newGreen, blue: newBlue, alpha: alpha)

}

}

#endif



//https://stackoverflow.com/questions/38435308/get-lighter-and-darker-color-variations-for-a-given-uicolor by lukszar
extension Comparable {

Expand Down
4 changes: 3 additions & 1 deletion Sources/SwiftUIGraphs/Model/OrientationObserver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import Foundation
import SwiftUI

#if os(iOS)
public final class OrientationObserver: ObservableObject {
enum Orientation {
case portrait
Expand All @@ -19,7 +20,7 @@ public final class OrientationObserver: ObservableObject {
private var _observer: NSObjectProtocol?

public init() {

if UIDevice.current.orientation.isLandscape {

self.orientation = .landscape
Expand Down Expand Up @@ -49,3 +50,4 @@ public final class OrientationObserver: ObservableObject {
}
}
}
#endif
126 changes: 43 additions & 83 deletions Sources/SwiftUIGraphs/Model/Settings/AxisSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,104 +10,64 @@ import SwiftUI

public struct YAxisSettings {

var showYAxis: Bool
var yAxisPosition: Edge.Set
var yAxisViewWidth: CGFloat
var showYAxisGridLines: Bool
var yAxisGridLinesStrokeStyle: StrokeStyle
var yAxisGridLineColor: Color
var labelFont: UIFont
var showYAxis: Bool = true
var yAxisPosition: Edge.Set = .leading
var yAxisViewWidth: CGFloat = 35
var showYAxisGridLines: Bool = true
var yAxisGridLinesStrokeStyle: StrokeStyle = StrokeStyle(lineWidth: 1, dash: [3])
var yAxisGridLineColor: Color = Color.secondary.opacity(0.5)
#if os(macOS)
var labelFont: NSFont = .systemFont(ofSize: 10)
#else
var labelFont: UIFont = .systemFont(ofSize: 8)
#endif
var yAxisMinMaxOverride: (min:Double?, max:Double?)? = nil
var yAxisIntervalOverride: Double? = nil

var yAxisMinMaxOverride: (min:Double?, max:Double?)?
var yAxisIntervalOverride: Double?

/// YAxisSettings
/// - Parameters:
/// - showYAxis: determines if the y-axis should be shown.
/// - yAxisPosition: y-axis position. can be leading or trailing.
/// - yAxisViewWidth: width of the y-axis view. Adjust if the y-axis labels don't fit.
/// - showYAxisGridLines: determines if the (horizontal) y-axis grid lines should be shown.
/// - yAxisGridLineStrokeStyle: stroke style of the y-axis grid lines.
/// - yAxisGridLineColor: color of the y-axis grid lines.
/// - labelFont:a UI font for all y-axis tick labels
/// - yAxisMinMaxOverride: override the max and min values of the y-axis. if not set, the min and max value will be calculated automatically.
/// - yAxisIntervalOverride: override the interval of the y-axis values. If not set, the interval will be calculated automatically.
public init(showYAxis: Bool = true, yAxisPosition: Edge.Set = .leading, yAxisViewWidth: CGFloat = 35, showYAxisGridLines: Bool = true, yAxisGridLineStrokeStyle: StrokeStyle = StrokeStyle(lineWidth: 1, dash: [3]), yAxisGridLineColor: Color = Color.secondary.opacity(0.5), labelFont: UIFont = UIFont.systemFont(ofSize: 8), yAxisMinMaxOverride: (min:Double?, max:Double?)? = nil, yAxisIntervalOverride: Double? = nil) {

self.showYAxis = showYAxis
self.yAxisPosition = yAxisPosition
self.yAxisViewWidth = yAxisViewWidth
self.showYAxisGridLines = showYAxisGridLines
self.yAxisGridLinesStrokeStyle = yAxisGridLineStrokeStyle
self.yAxisGridLineColor = yAxisGridLineColor
self.labelFont = labelFont
self.yAxisMinMaxOverride = yAxisMinMaxOverride
self.yAxisIntervalOverride = yAxisIntervalOverride

}
}

public protocol XAxisSettings {
protocol XAxisSettings {
var showXAxis: Bool {get set}
#if os(macOS)
var labelFont: NSFont {get set}
#else
var labelFont: UIFont {get set}
#endif
var xAxisViewHeight: CGFloat {get set}
}

public struct DYLineChartXAxisSettings: XAxisSettings {

public var showXAxis: Bool
public var xAxisViewHeight: CGFloat
var showXAxisGridLines: Bool
var xAxisGridLineStrokeStyle: StrokeStyle
var xAxisGridLineColor: Color
public var labelFont: UIFont
var minMaxOverride: (min:Double?, max:Double?)?
var intervalOverride: Double?

/// DYLineChartXAxisSettings initializer.
/// - Parameters:
/// - showXAxis: determines if the x axis should be shown.
/// - xAxisViewHeight: height of the xAxis view.
/// - showXAxisGridLines: determines if the x axis grid lines should be shown (vertical lines).
/// - xAxisGridLineStrokeStyle: stroke style of the vertical x axis grid lines.
/// - xAxisGridLineColor: color of the xAxis grid lines.
/// - labelFont: a ui font for all tick labels on the x-axis.
/// - minMaxOverride: override the max and min values of the x-axis. if not set, the min and max value will be calculated automatically.
/// - intervalOverride: override the interval of the x-axis values. If not set, the interval will be calculated automatically.
public init(showXAxis: Bool = true, xAxisViewHeight: CGFloat = 20, showXAxisGridLines: Bool = true, xAxisGridLineStrokeStyle: StrokeStyle = StrokeStyle(lineWidth: 1, dash: [3]), xAxisGridLineColor: Color = Color.secondary.opacity(0.5), labelFont: UIFont = UIFont.systemFont(ofSize: 8), minMaxOverride: (min:Double?, max:Double?)? = nil, intervalOverride: Double? = nil ) {

self.showXAxis = showXAxis
self.xAxisViewHeight = xAxisViewHeight
self.showXAxisGridLines = showXAxisGridLines
self.xAxisGridLineStrokeStyle = xAxisGridLineStrokeStyle
self.xAxisGridLineColor = xAxisGridLineColor
self.labelFont = labelFont
self.minMaxOverride = minMaxOverride
self.intervalOverride = intervalOverride
}
struct DYLineChartXAxisSettings: XAxisSettings {

var showXAxis: Bool = true
var xAxisViewHeight: CGFloat = 20
var showXAxisGridLines: Bool = true
var xAxisGridLineStrokeStyle: StrokeStyle = StrokeStyle(lineWidth: 1, dash: [3])
var xAxisGridLineColor: Color = Color.secondary.opacity(0.5)
#if os(macOS)
var labelFont: NSFont = .systemFont(ofSize: 10)
#else
var labelFont: UIFont = .systemFont(ofSize: 8)
#endif
var minMaxOverride: (min:Double?, max:Double?)? = nil
var intervalOverride: Double? = nil


}




/// Bar chart x-axis settings
public struct DYBarChartXAxisSettings: XAxisSettings {

public var showXAxis: Bool
public var xAxisViewHeight: CGFloat
public var labelFont: UIFont
struct DYBarChartXAxisSettings: XAxisSettings {

/// DYBarChartXAxisSettings
/// - Parameters:
/// - showXAxis: determines if the x-axis should be shown.
/// - xAxisViewHeight: height of the xAxis view.
/// - labelFont: a ui font for all tick labels on the x-axis.
public init(showXAxis: Bool = true, xAxisViewHeight:CGFloat = 20, labelFont: UIFont = UIFont.systemFont(ofSize: 8)) {

self.showXAxis = showXAxis
self.xAxisViewHeight = xAxisViewHeight
self.labelFont = labelFont
}
var showXAxis: Bool = true
var xAxisViewHeight: CGFloat = 20
#if os(macOS)
var labelFont: NSFont = .systemFont(ofSize: 10)
#else
var labelFont: UIFont = .systemFont(ofSize: 8)
#endif

}


Expand Down
59 changes: 19 additions & 40 deletions Sources/SwiftUIGraphs/Model/Settings/DYPlotAreaSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,47 +9,26 @@ import Foundation
import SwiftUI


public struct DYLineChartSettings {

public var plotAreaBackgroundGradient: LinearGradient
public var allowUserInteraction: Bool
var selectorLineWidth: CGFloat
var selectorLineColor: Color


public init(plotAreaBackgroundGradient: LinearGradient = LinearGradient(gradient: Gradient(colors: [Color(.systemBackground), Color(.systemBackground)]), startPoint: .top, endPoint: .bottom), selectorLineWidth: CGFloat = 2, selectorLineColor: Color = .red, allowUserInteraction: Bool = true) {
self.plotAreaBackgroundGradient = plotAreaBackgroundGradient
self.selectorLineWidth = selectorLineWidth
self.selectorLineColor = selectorLineColor
self.allowUserInteraction = allowUserInteraction

}


}
struct DYLineChartSettings {

public struct DYBarChartSettings {

public var plotAreaBackgroundGradient: LinearGradient
public var allowUserInteraction: Bool
var selectedBarBorderColor: Color
var barDropShadow: Shadow?
var selectedBarDropShadow: Shadow?
var labelViewOffset: CGSize
var minimumTopEdgeBarLabelMargin: CGFloat
var minimumBottomEdgeBarLabelMargin: CGFloat


public init(plotAreaBackgroundGradient: LinearGradient = LinearGradient(gradient: Gradient(colors: [Color(.systemBackground), Color(.systemBackground)]), startPoint: .top, endPoint: .bottom), allowUserInteraction: Bool = true, selectedBarBorderColor: Color = .yellow, barDropShadow: Shadow? = nil, selectedBarDropShadow: Shadow? = nil, labelViewOffset: CGSize = CGSize(width: 0, height: -10), minimumTopEdgeBarLabelMargin: CGFloat = 0, minimumBottomEdgeBarLabelMargin: CGFloat = 10) {
self.plotAreaBackgroundGradient = plotAreaBackgroundGradient
self.allowUserInteraction = allowUserInteraction
self.selectedBarBorderColor = selectedBarBorderColor
self.barDropShadow = barDropShadow
self.selectedBarDropShadow = selectedBarDropShadow
self.labelViewOffset = labelViewOffset
self.minimumTopEdgeBarLabelMargin = minimumTopEdgeBarLabelMargin
self.minimumBottomEdgeBarLabelMargin = minimumBottomEdgeBarLabelMargin
}
public var plotAreaBackgroundGradient: LinearGradient = LinearGradient(colors: [Color.defaultPlotAreaBackgroundColor, Color.defaultPlotAreaBackgroundColor], startPoint: .top, endPoint: .bottom)
public var allowUserInteraction: Bool = true
var selectorLineWidth: CGFloat = 2
var selectorLineColor: Color = .red


}

struct DYBarChartSettings {

var plotAreaBackgroundGradient: LinearGradient = LinearGradient(colors: [Color.defaultPlotAreaBackgroundColor, Color.defaultPlotAreaBackgroundColor], startPoint: .top, endPoint: .bottom)
var allowUserInteraction: Bool = true
var selectedBarBorderColor: Color = Color.yellow
var barDropShadow: Shadow? = nil
var selectedBarDropShadow: Shadow? = nil
var labelViewOffset: CGSize = CGSize(width: 0, height: -10)
var minimumTopEdgeBarLabelMargin: CGFloat = 0
var minimumBottomEdgeBarLabelMargin: CGFloat = 10

}

Loading

0 comments on commit 6f2e3b5

Please sign in to comment.