Last active
January 24, 2023 11:18
-
-
Save mattyoung/6ddb669cb2c131bb5d858f37d991561a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
// pre-create one instance of MeasurementFormatter for our format needs | |
// re-use as needed | |
extension MeasurementFormatter { | |
static private let formatter: MeasurementFormatter = { | |
let formatter = MeasurementFormatter() | |
let numberFormatter = NumberFormatter() | |
numberFormatter.numberStyle = .decimal | |
numberFormatter.minimumFractionDigits = 2 | |
numberFormatter.maximumFractionDigits = 2 | |
numberFormatter.roundingMode = .halfUp | |
formatter.numberFormatter = numberFormatter | |
return formatter | |
}() | |
static func formatter(unitOptions: MeasurementFormatter.UnitOptions = []) -> MeasurementFormatter { | |
Self.formatter.unitOptions = unitOptions | |
return Self.formatter | |
} | |
} | |
/// A slider to input any kind of Measurement value in any kind of Dimension unit such as UnitTemperature, UnitLength, UnitMass | |
/// within min, max range, optinal step value | |
struct DimensionSlider<UnitType: Dimension>: View { | |
@Binding var value: Measurement<UnitType> | |
let unit: UnitType | |
let min, max: Measurement<UnitType> | |
let step: Double // slider step size in the specified unit | |
init(value: Binding<Measurement<UnitType>>, unit: UnitType, min: Measurement<UnitType>, max: Measurement<UnitType>, step: Double = 0.5) { | |
self._value = value | |
self.unit = unit | |
self.min = min.converted(to: unit) | |
self.max = max.converted(to: unit) | |
self.step = step | |
} | |
var body: some View { | |
VStack { | |
// show value in specified unit | |
Text(MeasurementFormatter.formatter(unitOptions: .providedUnit).string(from: self.value.converted(to: unit))) | |
Slider(value: Binding<Double>(get: { self.value.converted(to: self.unit).value }, set: { self.value = Measurement(value: $0, unit: self.unit)}), | |
in: self.min.value...self.max.value, | |
step: self.step, | |
minimumValueLabel: Text("\(self.min.value, specifier: "%.2f")"), // show just the number without unit symbol | |
maximumValueLabel: Text("\(self.max.value, specifier: "%.2f")")) { | |
Text("Er, what's this for?") | |
} | |
} | |
} | |
} | |
private struct DisplayView<UnitType: Unit>: View { | |
let label: String | |
let value: Measurement<UnitType> | |
var body: some View { | |
HStack { | |
Text(label) | |
.font(.title) | |
Text(MeasurementFormatter.formatter().string(from: value)) | |
.font(Font.title.monospacedDigit()) | |
.foregroundColor(.green) | |
} | |
.padding() | |
.background(Color.gray) | |
.cornerRadius(10.0) | |
.padding(.vertical) | |
} | |
} | |
struct TemperatureSliderDemo: View { | |
// Using Measurement, you can specify temperature in any unit | |
@State private var temperature = Measurement(value: 273.15, unit: UnitTemperature.kelvin) | |
let minTemperature = Measurement(value: -100, unit: UnitTemperature.celsius) | |
let maxTemperature = Measurement(value: 300, unit: UnitTemperature.fahrenheit) | |
@State private var weight = Measurement(value: 26.7, unit: UnitMass.pounds) | |
let minWeight = Measurement(value: 2, unit: UnitMass.ounces) | |
let maxWeight = Measurement(value: 1000, unit: UnitMass.kilograms) | |
@State private var distance = Measurement(value: 368.98, unit: UnitLength.miles) | |
let minDistance = Measurement(value: 0, unit: UnitLength.meters) | |
let maxDistance = Measurement(value: 19000, unit: UnitLength.kilometers) | |
var body: some View { | |
VStack { | |
Group { | |
DisplayView(label: "Temperature", value: temperature) | |
DimensionSlider(value: $temperature, unit: UnitTemperature.fahrenheit, min: minTemperature, max: maxTemperature) | |
.padding(.horizontal) | |
DimensionSlider(value: $temperature, unit: UnitTemperature.celsius, min: minTemperature, max: maxTemperature) | |
.padding(.horizontal) | |
DimensionSlider(value: $temperature, unit: UnitTemperature.kelvin, min: minTemperature, max: maxTemperature) | |
.padding(.horizontal) | |
} | |
Spacer() | |
Group { | |
DisplayView(label: "Weight", value: weight) | |
DimensionSlider(value: $weight, unit: UnitMass.grams, min: minWeight, max: maxWeight, step: 10) | |
.padding(.horizontal) | |
DimensionSlider(value: $weight, unit: UnitMass.decigrams, min: minWeight, max: maxWeight, step: 100) | |
.padding(.horizontal) | |
DimensionSlider(value: $weight, unit: UnitMass.milligrams, min: minWeight, max: maxWeight, step: 30) | |
.padding(.horizontal) | |
} | |
Spacer() | |
Group { | |
DisplayView(label: "Distance", value: distance) | |
DimensionSlider(value: $distance, unit: UnitLength.inches, min: minDistance, max: maxDistance, step: 10) | |
.padding(.horizontal) | |
DimensionSlider(value: $distance, unit: UnitLength.fathoms, min: minDistance, max: maxDistance, step: 100) | |
.padding(.horizontal) | |
} | |
} | |
} | |
} | |
struct TemperatureSliderDemo_Previews: PreviewProvider { | |
static var previews: some View { | |
TemperatureSliderDemo() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment