Skip to content

Commit

Permalink
Merge pull request #1 from antonmartinsson/0.3
Browse files Browse the repository at this point in the history
Merge branch 0.3 into main
  • Loading branch information
antonmartinsson authored Dec 5, 2021
2 parents 20a0877 + 2243596 commit 20ee729
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 67 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# GaugeKit

GaugeKit is a Swift package which enables easy, effortless creation of a gauge-like view ideal for visualizing a value between 0 and 100 in a gauge,
GaugeKit is a Swift package which enables easy, effortless creation of a gauge-like view ideal for visualizing a value in a gauge,
not too different from the native gauges used by Apple for some Apple Watch complications.

## Importing GaugeKit
Expand All @@ -24,14 +24,20 @@ GaugeKit is built with SwiftUI, and thus the minimum requirement to use it is th

## Usage

To create a basic Gauge is as simple as providing it with a title, an integer value between 0 and 100, and the colors that you want the gauge display along itself. For example:
To create a basic Gauge is as simple as providing it with a title, an integer value, and the colors that you want the gauge display along itself. For example:

```swift
GaugeView(title: "Speed", value: 88, colors: [.red, .orange, .yellow, .green])
```

![alt text](https://i.imgur.com/iXPEpmm.png)

A basic gauge like this will default to 100 for its max value. You can also explicitely set the max value of the gauge to a custom value. The following initialization will create a gauge that maxes out at 1000 instead of 100.

```swift
GaugeView(title: "Speed", value: 100, maxValue: 1000, colors: [.red, .orange, .yellow, .green])
```

Additionally, as space for additional information is quite limited within the gauge, you can initialize a gauge with some additional information using three different (optional) strings. This information will be revealed to the user with a quick flip animation when a tap on the gauge view is recorded.

```swift
Expand Down Expand Up @@ -60,4 +66,4 @@ GaugeView(colors: [.red, .orange, .yellow, .green])

## Roadmap

While I don't have many concrete plans for GaugeKit at the moment and plan to just fiddle with it from time to time, I would like to add the ability to customize the scale instead of it being fixed between 0 and 100. Until I find the time and inspiration to do so, keep your eyes peeled on this repo.
While I don't have many concrete plans for GaugeKit at the moment and plan to just fiddle with it from time to time. If you have any feature requests or ideas you think I should take into consideration, please feel free to contact me here on GitHub.
6 changes: 4 additions & 2 deletions Sources/GaugeKit/GaugeBackView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ public struct GaugeAdditionalInfo {
*/
struct GaugeBackView: View {
@Binding var flipped: Bool
var additionalInfo: GaugeAdditionalInfo
let additionalInfo: GaugeAdditionalInfo

var body: some View {
let flipAngle = Angle(degrees: flipped ? -180 : 0)

GeometryReader { geometry in
VStack {
if let preTitle = additionalInfo.preTitle {
Expand All @@ -42,7 +44,7 @@ struct GaugeBackView: View {
.position(x: geometry.size.width / 2, y: geometry.size.height / 2)
}
.opacity(flipped ? 1 : 0)
.rotation3DEffect(Angle(degrees: flipped ? -180 : 0), axis: (x: 0, y: 1, z: 0))
.rotation3DEffect(flipAngle, axis: (x: 0, y: 1, z: 0))
.rotation3DEffect(Angle(degrees: 180), axis: (x: 0, y: 1, z: 0))
}
}
Expand Down
38 changes: 30 additions & 8 deletions Sources/GaugeKit/GaugeExtension.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//
// File.swift
// File
// GaugeExtension.swift
// GaugeExtension
//
// Created by Anton Martinsson on 2021-09-03.
//
Expand All @@ -12,33 +12,54 @@ import SwiftUI
extension GaugeView {

/**
Initializes a gauge with a string, a value and an array of colors to create a background gradient from.
Initializes a gauge with a value and an array of colors to create a background gradient from.
Defaults the max value to 100.
- Parameters:
- title: A short title that will be displayed in the center of the gauge, below its value.
- value: An integer between 0 and 100 to visualize using the gauge.
- colors: An array of Color instances to create the gauge's background gradient from..
*/
init(title: String?, value: Int?, colors: [Color]) {
public init(value: Int?, colors: [Color]) {
self.title = nil
self.value = value
self.maxValue = 100
self.colors = colors
self.additionalInfo = nil
}

/**
Initializes a gauge with a title, a value and an array of colors to create a background gradient from.
Defaults the max value to 100.
- Parameters:
- title: A short title that will be displayed in the center of the gauge, below its value.
- value: An integer between 0 and 100 to visualize using the gauge.
- colors: An array of Color instances to create the gauge's background gradient from..
*/
public init(title: String?, value: Int?, colors: [Color]) {
self.title = title
self.value = value
self.maxValue = 100
self.colors = colors
self.additionalInfo = nil
}

/**
Initializes a gauge with a string, a value, an array of colors to create a background gradient from,
as well as some additional information to display on the back of the gauge once it is tapped by the user..
Initializes a gauge with a title, a value, an array of colors to create a background gradient from,
as well as some additional information to display on the back of the gauge once it is tapped by the user.
Defaults the max value to 100.
- Parameters:
- title: A short title that will be displayed in the center of the gauge, below its value.
- value: An integer between 0 and 100 to visualize using the gauge.
- colors: An array of Color instances to create the gauge's background gradient from..
- additionalInfo: A struct that contains three optional strings to display on the back of the gauge.
*/
init(title: String?, value: Int?, colors: [Color], additionalInfo: GaugeAdditionalInfo) {
public init(title: String?, value: Int?, colors: [Color], additionalInfo: GaugeAdditionalInfo) {
self.title = title
self.value = value
self.maxValue = 100
self.colors = colors
self.additionalInfo = additionalInfo
}
Expand All @@ -49,9 +70,10 @@ extension GaugeView {
- Parameters:
- colors: An array of Color instances to create the gauge's background gradient from..
*/
init(colors: [Color]) {
public init(colors: [Color]) {
self.title = nil
self.value = nil
self.maxValue = 0
self.colors = colors
self.additionalInfo = nil
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/GaugeKit/GaugeIndicator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import SwiftUI
The small circular indicator placed on top of the gauge to visualize it's current value.
- Parameters:
- angle: The angle at which to place the indicator on top of the gauge.
- size: The size of the gauge being displayed.
- angle: The angle at which to place the indicator on top of the gauge.
- size: The size of the gauge being displayed.
*/
struct GaugeIndicator: View {
var angle: Angle?
Expand All @@ -36,7 +36,7 @@ struct GaugeIndicator: View {
A custom ViewModifier mainly created to declutter the amount of attributes on the indicator slightly.
- Parameters:
- size: The size of the gauge being displayed.
- size: The size of the gauge being displayed.
*/
private struct IndicatorPlacement: ViewModifier {
var size: CGSize
Expand Down
15 changes: 5 additions & 10 deletions Sources/GaugeKit/GaugeLabelStack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ import SwiftUI
A simple vertical stack of labels to be stashed within the gauge view.
- Parameters:
- containerSize: The size of the gauge, as provided by a GeometryReader instance in Gauge.
- geometry: The frame of the container the label stack is contained within.
- value: An integer between 0 and 100 displayed inside the gauge.
- title: A title to be displayed below the value.
*/
struct GaugeLabelStack: View {
var containerSize: CGSize
var geometry: GeometryProxy
var value: Int?
var title: String?

var body: some View {
let isTaller = containerSize.width < containerSize.height
let smallestDimension = isTaller ? containerSize.width : containerSize.height
let isTaller = geometry.size.width < geometry.size.height
let smallestDimension = isTaller ? geometry.size.width : geometry.size.height

VStack {
if let unwrappedValue = value {
Expand All @@ -36,11 +36,6 @@ struct GaugeLabelStack: View {
.font(.system(size: smallestDimension / 20))
}
}
.position(x: geometry.size.width / 2, y: geometry.size.height / 2)
}
}

struct GaugeLabelStack_Previews: PreviewProvider {
static var previews: some View {
GaugeLabelStack(containerSize: CGSize(width: 500, height: 500), value: 50, title: "Neutral")
}
}
43 changes: 28 additions & 15 deletions Sources/GaugeKit/GaugeMeter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,29 @@
import SwiftUI

/**
The circular meter of the gauge view.
The circular meter of the gauge view.
- Parameters:
- Parameters:
- value: An integer between 0 and 100 displayed inside the gauge, which also determines the position of the gauge's indicator.
- colors: The colors that should be used in the gradient that wipes across the gauge.
- maxValue: The value the gauge should top out at.
*/

struct GaugeMeter : View {
var value: Int?
var colors: [Color]
let value: Int?
let colors: [Color]
let maxValue: Int?

let trimStart = 0.1
let trimEnd = 0.9

init(value: Int? = nil, maxValue: Int? = nil, colors: [Color]) {
self.colors = colors
self.value = value
let defaultMaxValue = maxValue == nil && value != nil
self.maxValue = defaultMaxValue ? 100 : maxValue
}

var body: some View {
let startAngle = 360 * trimStart
let endAngle = 360 * trimEnd
Expand All @@ -45,8 +54,13 @@ struct GaugeMeter : View {
.shadow(color: .black.opacity(0.2), radius: 10, x: 0, y: 0)


if let unwrappedValue = value {
let degrees = (Double(unwrappedValue) * 2.88)
if let unwrappedValue = value, let unwrappedMax = maxValue {
let oneUnit = (Double(360) * 0.8) / Double(unwrappedMax)
let degrees = (Double(unwrappedValue) * oneUnit)
GaugeIndicator(angle: Angle(degrees: degrees), size: geometry.size)
} else if let unwrappedValue = value {
let oneUnit = (Double(360) * 0.8) / Double(100)
let degrees = (Double(unwrappedValue) * oneUnit)
GaugeIndicator(angle: Angle(degrees: degrees), size: geometry.size)
}
}
Expand All @@ -58,14 +72,14 @@ struct GaugeMeter : View {
/** A circular view used as a mask over the angular gradient of the GaugeMeter.
- Parameters:
- trimStart: A double between 0 and 1 that determines where the meter should begin.
- trimEnd: A double between 0 and 1 that determines where the meter should end.
- meterThickness: The thickness of the gauge meter.
- trimStart: A double between 0 and 1 that determines where the meter should begin.
- trimEnd: A double between 0 and 1 that determines where the meter should end.
- meterThickness: The thickness of the gauge meter.
*/
private struct GaugeMask: View {
var trimStart: Double
var trimEnd: Double
var meterThickness: Double
let trimStart: Double
let trimEnd: Double
let meterThickness: Double

var body: some View {
Circle()
Expand All @@ -78,8 +92,7 @@ private struct GaugeMask: View {

struct GaugeComponents_Previews: PreviewProvider {
static var previews: some View {
GaugeView(title: "Speed",
value: 88,
colors: [.red, .orange, .yellow, .green])
let colors: [Color] = [.red, .orange, .yellow, .green]
GaugeView(title: "Speed", value: 88, colors: colors)
}
}
36 changes: 19 additions & 17 deletions Sources/GaugeKit/GaugeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,61 @@
//
// Created by Anton Martinsson on 2021-06-19.
//
// A Gauge similar to the gauges used for some Apple Watch complication.
// A Gauge similar to the gauges used for some Apple Watch complications.

import SwiftUI

/**
A view ideal for visualizing a value between 0 and 100 in a gauge,
A view ideal for visualizing a value between in a gauge,
not too different from the native gauges used by Apple for some Apple Watch complications.
- Parameters:
- title: A decriptive string value to display inside the gauge.
- value: An integer between 0 and 100 displayed inside the gauge, which also determines the position of the gauge's indicator.
- value: An integer displayed inside the gauge, which also determines the position of the gauge's indicator.
- maxValue: An integer value representing what the gauge should max out at. Defaults to nil if `value` is also nil, and to 100 if a `value` is set, but no explicit `maxValue`.
- colors: The colors that should be used in the gradient that wipes across the gauge.
- additionalInfo: A struct containing three (optional) strings to display when the user taps on the gauge.
*/
public struct GaugeView : View {
@State private var flipped: Bool = false

public var title: String?
public var value: Int?
public var colors: [Color]
public var additionalInfo: GaugeAdditionalInfo?
let title: String?
let value: Int?
let maxValue: Int?
let colors: [Color]
let additionalInfo: GaugeAdditionalInfo?

public init(title: String? = nil,
value: Int? = nil,
maxValue: Int? = nil,
colors: [Color],
additionalInfo: GaugeAdditionalInfo? = nil) {
self.title = title
self.value = value
self.maxValue = maxValue
self.colors = colors
self.additionalInfo = additionalInfo
}

public var body: some View {
let flipAngle = Angle(degrees: flipped ? 180 : 0)

ZStack {
ZStack {
GaugeMeter(value: value, colors: colors)
GaugeMeter(value: value, maxValue: maxValue, colors: colors)
GeometryReader { geometry in
GaugeLabelStack(containerSize: geometry.size, value: value, title: title)
.position(x: geometry.size.width / 2, y: geometry.size.height / 2)
GaugeLabelStack(geometry: geometry, value: value, title: title)
}
}
.rotation3DEffect(Angle(degrees: flipped ? 180 : 0), axis: (x: 0, y: 1, z: 0))
.rotation3DEffect(flipAngle, axis: (x: 0, y: 1, z: 0))
.opacity(flipped ? 0.1 : 1)

if let info = additionalInfo {
GaugeBackView(flipped: $flipped, additionalInfo: info)
}
}

.if(additionalInfo != nil) { content in
content.onTapGesture {
.onTapGesture {
if additionalInfo != nil {
withAnimation {
self.flipped.toggle()
}
Expand All @@ -65,8 +69,6 @@ public struct GaugeView : View {

struct Gauge_Previews: PreviewProvider {
static var previews: some View {
GaugeView(title: "BTC F&G",
value: 50,
colors: [.red, .orange, .yellow, .green])
GaugeView(value: 50, colors: [.red, .orange, .yellow, .green])
}
}
9 changes: 0 additions & 9 deletions Sources/GaugeKit/ViewExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,6 @@ extension View {
}
}

@ViewBuilder
func `if`<Content: View>(_ conditional: Bool, content: (Self) -> Content) -> some View {
if conditional {
content(self)
} else {
self
}
}

func widgetify() -> some View {
self.modifier(RoundCornersAndAddShadows())
}
Expand Down
Loading

0 comments on commit 20ee729

Please sign in to comment.