// // 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: 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) }