diff --git a/Ratios/Ratios.xcodeproj/project.pbxproj b/Ratios/Ratios.xcodeproj/project.pbxproj index e4d3963..880fd0c 100644 --- a/Ratios/Ratios.xcodeproj/project.pbxproj +++ b/Ratios/Ratios.xcodeproj/project.pbxproj @@ -24,6 +24,8 @@ 52712496240B566D00F26EF3 /* CalculatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52712495240B566D00F26EF3 /* CalculatorTests.swift */; }; 52712498240B58CF00F26EF3 /* WaterInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52712497240B58CF00F26EF3 /* WaterInput.swift */; }; 5271249A240B663A00F26EF3 /* WaterDisplay.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52712499240B663A00F26EF3 /* WaterDisplay.swift */; }; + AE92CF722C1DAD9F005124AB /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE92CF712C1DAD9F005124AB /* File.swift */; }; + AEA2EDDA2C1D0A860094972B /* DecimalInputModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEA2EDCC2C1D085F0094972B /* DecimalInputModifier.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -67,6 +69,8 @@ 52712495240B566D00F26EF3 /* CalculatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalculatorTests.swift; sourceTree = ""; }; 52712497240B58CF00F26EF3 /* WaterInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaterInput.swift; sourceTree = ""; }; 52712499240B663A00F26EF3 /* WaterDisplay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaterDisplay.swift; sourceTree = ""; }; + AE92CF712C1DAD9F005124AB /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = SOURCE_ROOT; }; + AEA2EDCC2C1D085F0094972B /* DecimalInputModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecimalInputModifier.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -117,6 +121,7 @@ 526BD848240771A100A24AD2 /* Ratios */ = { isa = PBXGroup; children = ( + AEA2EDCB2C1D08380094972B /* Modifiers */, 52712490240B554D00F26EF3 /* View Models */, 526BD87C24077F3300A24AD2 /* Views */, 526BD849240771A100A24AD2 /* AppDelegate.swift */, @@ -161,6 +166,7 @@ 526BD87C24077F3300A24AD2 /* Views */ = { isa = PBXGroup; children = ( + AE92CF712C1DAD9F005124AB /* File.swift */, 526BD87D24077F4200A24AD2 /* TimerView.swift */, 526BD8832408CCFE00A24AD2 /* CoffeeInput.swift */, 526BD8852408D40200A24AD2 /* RatioInput.swift */, @@ -179,6 +185,14 @@ path = "View Models"; sourceTree = ""; }; + AEA2EDCB2C1D08380094972B /* Modifiers */ = { + isa = PBXGroup; + children = ( + AEA2EDCC2C1D085F0094972B /* DecimalInputModifier.swift */, + ); + path = Modifiers; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -242,7 +256,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1130; - LastUpgradeCheck = 1130; + LastUpgradeCheck = 1240; ORGANIZATIONNAME = "John Peden"; TargetAttributes = { 526BD845240771A100A24AD2 = { @@ -313,12 +327,14 @@ 526BD8842408CCFE00A24AD2 /* CoffeeInput.swift in Sources */, 52712492240B563000F26EF3 /* CalculatorViewModel.swift in Sources */, 52712498240B58CF00F26EF3 /* WaterInput.swift in Sources */, + AEA2EDDA2C1D0A860094972B /* DecimalInputModifier.swift in Sources */, 526BD87E24077F4200A24AD2 /* TimerView.swift in Sources */, 526BD84A240771A100A24AD2 /* AppDelegate.swift in Sources */, 5271249A240B663A00F26EF3 /* WaterDisplay.swift in Sources */, 526BD84F240771A100A24AD2 /* Ratios.xcdatamodeld in Sources */, 526BD851240771A100A24AD2 /* ContentView.swift in Sources */, 526BD8862408D40200A24AD2 /* RatioInput.swift in Sources */, + AE92CF722C1DAD9F005124AB /* File.swift in Sources */, 526BD88224079A5B00A24AD2 /* TimerViewModel.swift in Sources */, 526BD84C240771A100A24AD2 /* SceneDelegate.swift in Sources */, ); @@ -394,6 +410,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; @@ -454,6 +471,7 @@ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; diff --git a/Ratios/Ratios/Modifiers/DecimalInputModifier.swift b/Ratios/Ratios/Modifiers/DecimalInputModifier.swift new file mode 100644 index 0000000..2a2dc1d --- /dev/null +++ b/Ratios/Ratios/Modifiers/DecimalInputModifier.swift @@ -0,0 +1,33 @@ +// +// DecimalInput.swift +// Ratios +// +// Created by Yoonhee Tian on 2024-06-14. +// Copyright © 2024 John Peden. All rights reserved. +// + +import SwiftUI +import Combine + +struct DecimalInputModifier: ViewModifier { + @Binding var text: String + + func body(content: Content) -> some View { + AnyView(content) + .onReceive(Just(text)) { newValue in + var filtered = newValue.filter { "0123456789.".contains($0) } + if (filtered.filter { $0 == "." }.count > 1) { + filtered = String(filtered.dropLast()) + } + if filtered != newValue { + self.text = filtered + } + } + } +} + +extension View { + func decimalInput(text: Binding) -> some View { + self.modifier(DecimalInputModifier(text: text)) + } +} diff --git a/Ratios/Ratios/Views/CoffeeInput.swift b/Ratios/Ratios/Views/CoffeeInput.swift index 0a4cf3d..c40f4b4 100644 --- a/Ratios/Ratios/Views/CoffeeInput.swift +++ b/Ratios/Ratios/Views/CoffeeInput.swift @@ -30,16 +30,7 @@ struct CoffeeInput: View { ) .multilineTextAlignment(.center) .keyboardType(.decimalPad) - .onReceive(Just(amount)) { newValue in - - - - let filtered = newValue.filter { "0123456789.0".contains($0) } - if filtered != newValue { - self.amount = filtered - } - } - + .decimalInput(text: $amount) Text("grams") .fixedSize() diff --git a/Ratios/Ratios/Views/WaterInput.swift b/Ratios/Ratios/Views/WaterInput.swift index 72f8b65..9ad6ea4 100644 --- a/Ratios/Ratios/Views/WaterInput.swift +++ b/Ratios/Ratios/Views/WaterInput.swift @@ -38,6 +38,7 @@ struct WaterInput: View { VStack(alignment: .center) { TextField("", text: $amount) + .decimalInput(text: $amount) .frame(width: CGFloat(39), height: CGFloat(39)) .overlay( RoundedRectangle(cornerRadius: 10) @@ -46,12 +47,7 @@ struct WaterInput: View { .multilineTextAlignment(.center) .font(.system(size: 24)) .keyboardType(.decimalPad) - .onReceive(Just(amount)) { newValue in - let filtered = newValue.filter { "0123456789.0".contains($0) } - if filtered != newValue { - self.amount = filtered - } - } + Text("water") .fixedSize()