add ValueKeyboard as input for exercise values
This commit is contained in:
175
WorkoutsPlus/Components/ValueKeyboard.swift
Normal file
175
WorkoutsPlus/Components/ValueKeyboard.swift
Normal file
@@ -0,0 +1,175 @@
|
||||
//
|
||||
// ValuePickerKeyboard.swift
|
||||
// WorkoutsPlus
|
||||
//
|
||||
// Created by Felix Förtsch on 24.09.24.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol ValueType {
|
||||
associatedtype UnitType: CaseIterable & Hashable & CustomStringConvertible
|
||||
var value: String { get set }
|
||||
var unit: UnitType { get set }
|
||||
}
|
||||
|
||||
enum ExerciseUnit: String, CaseIterable, CustomStringConvertible {
|
||||
case reps = "reps"
|
||||
case meter = "m"
|
||||
case second = "s"
|
||||
case speed = "m/s"
|
||||
case pace = "s/m"
|
||||
|
||||
var description: String { rawValue }
|
||||
}
|
||||
|
||||
struct ExerciseValue: ValueType {
|
||||
var value: String = ""
|
||||
var unit: ExerciseUnit = .reps
|
||||
}
|
||||
|
||||
enum FoodUnit: String, CaseIterable, CustomStringConvertible {
|
||||
case gram = "g"
|
||||
case milliliter = "ml"
|
||||
|
||||
var description: String { rawValue }
|
||||
}
|
||||
|
||||
struct FoodValue: ValueType {
|
||||
var value: String = ""
|
||||
var unit: FoodUnit = .gram
|
||||
}
|
||||
|
||||
struct KeyboardButtonStyle: ButtonStyle {
|
||||
var width: CGFloat = 100
|
||||
var height: CGFloat = 50
|
||||
var color: Color = Color(.systemGray6)
|
||||
var font: Font = .system(size: 24, weight: .bold, design: .rounded)
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.label
|
||||
.frame(width: width, height: height)
|
||||
.background(color)
|
||||
.cornerRadius(8)
|
||||
.shadow(color: .gray, radius: 1, x: 0, y: 1)
|
||||
.scaleEffect(configuration.isPressed ? 0.95 : 1.0)
|
||||
.font(font)
|
||||
}
|
||||
}
|
||||
|
||||
struct ValueKeyboard<Value: ValueType>: View {
|
||||
@Binding var isPresented: Bool
|
||||
@Binding var value: Value
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.fill(Color(.systemGray5))
|
||||
.frame(height: 80)
|
||||
|
||||
HStack {
|
||||
TextField("0", text: $value.value)
|
||||
.font(.system(size: 32, weight: .bold, design: .rounded))
|
||||
.padding()
|
||||
|
||||
Picker("Unit", selection: $value.unit) {
|
||||
ForEach(Array(Value.UnitType.allCases), id: \.self) { unit in
|
||||
Text(unit.description).tag(unit)
|
||||
}
|
||||
}
|
||||
.pickerStyle(WheelPickerStyle())
|
||||
.frame(width: 125, height: 125)
|
||||
}
|
||||
.padding()
|
||||
.mask(RoundedRectangle(cornerRadius: 8).frame(height: 80))
|
||||
}
|
||||
.padding()
|
||||
.frame(height: 100)
|
||||
|
||||
VStack(spacing: 20) {
|
||||
HStack(spacing: 20) {
|
||||
Button(action: { handleButtonTap("1") }) {
|
||||
Text("1")
|
||||
}
|
||||
Button(action: { handleButtonTap("2") }) {
|
||||
Text("2")
|
||||
}
|
||||
Button(action: { handleButtonTap("3") }) {
|
||||
Text("3")
|
||||
}
|
||||
}
|
||||
|
||||
HStack(spacing: 20) {
|
||||
Button(action: { handleButtonTap("4") }) {
|
||||
Text("4")
|
||||
}
|
||||
Button(action: { handleButtonTap("5") }) {
|
||||
Text("5")
|
||||
}
|
||||
Button(action: { handleButtonTap("6") }) {
|
||||
Text("6")
|
||||
}
|
||||
}
|
||||
|
||||
HStack(spacing: 20) {
|
||||
Button(action: { handleButtonTap("7") }) {
|
||||
Text("7")
|
||||
}
|
||||
Button(action: { handleButtonTap("8") }) {
|
||||
Text("8")
|
||||
}
|
||||
Button(action: { handleButtonTap("9") }) {
|
||||
Text("9")
|
||||
}
|
||||
}
|
||||
|
||||
HStack(spacing: 20) {
|
||||
Button(action: { handleButtonTap("⌫") }) {
|
||||
Text("⌫")
|
||||
}
|
||||
|
||||
Button(action: { handleButtonTap("0") }) {
|
||||
Text("0")
|
||||
}
|
||||
|
||||
Button(action: { handleButtonTap("→") }) {
|
||||
Text("→")
|
||||
}
|
||||
.buttonStyle(KeyboardButtonStyle(color: .blue))
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
}
|
||||
.buttonStyle(KeyboardButtonStyle())
|
||||
}
|
||||
}
|
||||
|
||||
private func handleButtonTap(_ button: String) {
|
||||
switch button {
|
||||
case "⌫":
|
||||
if !value.value.isEmpty {
|
||||
value.value.removeLast()
|
||||
}
|
||||
case "→":
|
||||
isPresented.toggle()
|
||||
default:
|
||||
value.value.append(button)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("ExerciseValue") {
|
||||
@Previewable @State var exerciseValue: ExerciseValue = .init()
|
||||
@Previewable @State var isPresented: Bool = true
|
||||
Text(exerciseValue.value)
|
||||
Text(exerciseValue.unit.rawValue)
|
||||
ValueKeyboard(isPresented: $isPresented, value: $exerciseValue)
|
||||
}
|
||||
|
||||
#Preview("FoodValue") {
|
||||
@Previewable @State var foodValue: FoodValue = .init()
|
||||
@Previewable @State var isPresented: Bool = true
|
||||
Text(foodValue.value)
|
||||
Text(foodValue.unit.rawValue)
|
||||
ValueKeyboard(isPresented: $isPresented, value: $foodValue)
|
||||
}
|
||||
Reference in New Issue
Block a user