CarbsView.swift 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import SwiftUI
  2. struct CarbsView: View {
  3. @EnvironmentObject var state: WatchStateModel
  4. // Selected nutrient
  5. enum Selection: String {
  6. case none
  7. case carbs
  8. case protein
  9. case fat
  10. }
  11. @State var selection: Selection = .carbs
  12. @State var carbAmount = 0.0
  13. @State var fatAmount = 0.0
  14. @State var proteinAmount = 0.0
  15. @State var colorOfselection: Color = .darkGray
  16. // @State var displayPresets: Bool = false
  17. var numberFormatter: NumberFormatter {
  18. let formatter = NumberFormatter()
  19. formatter.numberStyle = .decimal
  20. formatter.minimum = 0
  21. formatter.maximum = (state.maxCOB ?? 120) as NSNumber
  22. formatter.maximumFractionDigits = 0
  23. formatter.allowsFloats = false
  24. return formatter
  25. }
  26. var body: some View {
  27. VStack {
  28. // nutrient
  29. carbs
  30. if state.displayFatAndProteinOnWatch {
  31. Spacer()
  32. protein
  33. Spacer()
  34. fat
  35. }
  36. buttonStack
  37. }
  38. .onAppear { carbAmount = Double(state.carbsRequired ?? 0) }
  39. }
  40. var nutrient: some View {
  41. HStack {
  42. switch selection {
  43. case .protein:
  44. Text("Protein")
  45. case .fat:
  46. Text("Fat")
  47. default:
  48. Text("Carbs")
  49. }
  50. }.font(.footnote).frame(maxWidth: .infinity, alignment: .center)
  51. }
  52. var carbs: some View {
  53. HStack {
  54. if selection == .carbs {
  55. Button {
  56. WKInterfaceDevice.current().play(.click)
  57. let newValue = carbAmount - 5
  58. carbAmount = max(newValue, 0)
  59. } label: { Image(systemName: "minus") }
  60. .buttonStyle(.borderless).padding(.leading, 5)
  61. .tint(selection == .carbs ? .blue : .none)
  62. }
  63. Spacer()
  64. Text("🥨")
  65. Spacer()
  66. Text(numberFormatter.string(from: carbAmount as NSNumber)! + " g")
  67. .font(selection == .carbs ? .title : .title3)
  68. .focusable(selection == .carbs)
  69. .digitalCrownRotation(
  70. $carbAmount,
  71. from: 0,
  72. through: Double(state.maxCOB ?? 120),
  73. by: 1,
  74. sensitivity: .medium,
  75. isContinuous: false,
  76. isHapticFeedbackEnabled: true
  77. )
  78. Spacer()
  79. if selection == .carbs {
  80. Button {
  81. WKInterfaceDevice.current().play(.click)
  82. let newValue = carbAmount + 5
  83. carbAmount = min(newValue, Double(state.maxCOB ?? 120))
  84. } label: { Image(systemName: "plus") }
  85. .buttonStyle(.borderless).padding(.trailing, 5)
  86. .tint(selection == .carbs ? .blue : .none)
  87. }
  88. }
  89. .onTapGesture {
  90. select(entry: .carbs)
  91. }
  92. .background(selection == .carbs && state.displayFatAndProteinOnWatch ? colorOfselection : .black)
  93. .padding(.top)
  94. }
  95. var protein: some View {
  96. HStack {
  97. if selection == .protein {
  98. Button {
  99. WKInterfaceDevice.current().play(.click)
  100. let newValue = proteinAmount - 5
  101. proteinAmount = max(newValue, 0)
  102. } label: { Image(systemName: "minus") }
  103. .buttonStyle(.borderless).padding(.leading, 5)
  104. .tint(selection == .protein ? .blue : .none)
  105. }
  106. Spacer()
  107. Text("🍗")
  108. Spacer()
  109. Text(numberFormatter.string(from: proteinAmount as NSNumber)! + " g")
  110. .font(selection == .protein ? .title : .title3)
  111. .foregroundStyle(.red)
  112. .focusable(selection == .protein)
  113. .digitalCrownRotation(
  114. $proteinAmount,
  115. from: 0,
  116. through: Double(240),
  117. by: 1,
  118. sensitivity: .medium,
  119. isContinuous: false,
  120. isHapticFeedbackEnabled: true
  121. )
  122. Spacer()
  123. if selection == .protein {
  124. Button {
  125. WKInterfaceDevice.current().play(.click)
  126. let newValue = proteinAmount + 5
  127. proteinAmount = min(newValue, Double(240))
  128. } label: { Image(systemName: "plus") }.buttonStyle(.borderless).padding(.trailing, 5)
  129. .tint(selection == .protein ? .blue : .none)
  130. }
  131. }
  132. .onTapGesture {
  133. select(entry: .protein)
  134. }
  135. .background(selection == .protein ? colorOfselection : .black)
  136. }
  137. var fat: some View {
  138. HStack {
  139. if selection == .fat {
  140. Button {
  141. WKInterfaceDevice.current().play(.click)
  142. let newValue = fatAmount - 5
  143. fatAmount = max(newValue, 0)
  144. } label: { Image(systemName: "minus") }
  145. .buttonStyle(.borderless).padding(.leading, 5)
  146. .tint(selection == .fat ? .blue : .none)
  147. }
  148. Spacer()
  149. Text("🧀")
  150. Spacer()
  151. Text(numberFormatter.string(from: fatAmount as NSNumber)! + " g")
  152. .font(selection == .fat ? .title : .title3)
  153. .foregroundColor(.loopYellow)
  154. .focusable(selection == .fat)
  155. .digitalCrownRotation(
  156. $fatAmount,
  157. from: 0,
  158. through: Double(240),
  159. by: 1,
  160. sensitivity: .medium,
  161. isContinuous: false,
  162. isHapticFeedbackEnabled: true
  163. )
  164. Spacer()
  165. if selection == .fat {
  166. Button {
  167. WKInterfaceDevice.current().play(.click)
  168. let newValue = fatAmount + 5
  169. fatAmount = min(newValue, Double(240))
  170. } label: { Image(systemName: "plus") }
  171. .buttonStyle(.borderless).padding(.trailing, 5)
  172. .tint(selection == .fat ? .blue : .none)
  173. }
  174. }
  175. .onTapGesture {
  176. select(entry: .fat)
  177. }
  178. .background(selection == .fat ? colorOfselection : .black)
  179. }
  180. var buttonStack: some View {
  181. HStack(spacing: 25) {
  182. /* To do: display the actual meal presets
  183. Button {
  184. displayPresets.toggle()
  185. }
  186. label: { Image(systemName: "menucard.fill") }
  187. .buttonStyle(.borderless)
  188. */
  189. Button {
  190. WKInterfaceDevice.current().play(.click)
  191. // Get amount from displayed string
  192. let amountCarbs = Int(numberFormatter.string(from: carbAmount as NSNumber)!) ?? Int(carbAmount.rounded())
  193. let amountFat = Int(numberFormatter.string(from: fatAmount as NSNumber)!) ?? Int(fatAmount.rounded())
  194. let amountProtein = Int(numberFormatter.string(from: proteinAmount as NSNumber)!) ??
  195. Int(proteinAmount.rounded())
  196. state.addMeal(amountCarbs, fat: amountFat, protein: amountProtein)
  197. }
  198. label: { Text("Save") }
  199. .buttonStyle(.borderless)
  200. .font(.callout)
  201. .foregroundColor(carbAmount > 0 || fatAmount > 0 || proteinAmount > 0 ? .blue : .secondary)
  202. .disabled(carbAmount <= 0 && fatAmount <= 0 && proteinAmount <= 0)
  203. }
  204. .frame(maxHeight: .infinity, alignment: .bottom)
  205. .padding(.top)
  206. }
  207. private func select(entry: Selection) {
  208. selection = entry
  209. }
  210. }
  211. struct CarbsView_Previews: PreviewProvider {
  212. static var previews: some View {
  213. let state = WatchStateModel()
  214. state.carbsRequired = 120
  215. return Group {
  216. CarbsView()
  217. CarbsView().previewDevice("Apple Watch Series 5 - 40mm")
  218. CarbsView().previewDevice("Apple Watch Series 3 - 38mm")
  219. }
  220. .environmentObject(state)
  221. }
  222. }