CheckmarkListItem.swift 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. //
  2. // CheckmarkListItem.swift
  3. // LoopKitUI
  4. //
  5. // Created by Rick Pasetto on 7/17/20.
  6. // Copyright © 2020 LoopKit Authors. All rights reserved.
  7. //
  8. import SwiftUI
  9. public struct CheckmarkListItem: View {
  10. var title: Text
  11. var titleFont: Font
  12. var description: Text
  13. @Binding var isSelected: Bool
  14. let isEnabled: Bool
  15. public init(title: Text, titleFont: Font = .headline, description: Text, isSelected: Binding<Bool>, isEnabled: Bool = true) {
  16. self.title = title
  17. self.titleFont = titleFont
  18. self.description = description
  19. self._isSelected = isSelected
  20. self.isEnabled = isEnabled
  21. }
  22. @ViewBuilder
  23. public var body: some View {
  24. if isEnabled {
  25. Button(action: { self.isSelected = true }) {
  26. content
  27. }
  28. } else {
  29. content
  30. }
  31. }
  32. private var content: some View {
  33. HStack(spacing: 0) {
  34. VStack(alignment: .leading, spacing: 4) {
  35. title
  36. .font(titleFont)
  37. description
  38. .font(.footnote)
  39. .foregroundColor(.secondary)
  40. .fixedSize(horizontal: false, vertical: true)
  41. .multilineTextAlignment(.leading)
  42. }
  43. Spacer(minLength: 12)
  44. selectionIndicator
  45. .accessibility(label: Text(isSelected ? "Selected" : "Unselected"))
  46. }
  47. .animation(nil)
  48. }
  49. @ViewBuilder
  50. private var selectionIndicator: some View {
  51. if isEnabled {
  52. filledCheckmark
  53. .frame(width: 26, height: 26)
  54. } else {
  55. plainCheckmark
  56. .frame(width: 22, height: 22)
  57. }
  58. }
  59. @ViewBuilder
  60. private var filledCheckmark: some View {
  61. if isSelected {
  62. Image(systemName: "checkmark.circle.fill")
  63. .resizable()
  64. .background(Circle().stroke()) // Ensure size aligns with open circle
  65. .foregroundColor(.accentColor)
  66. } else {
  67. Circle()
  68. .stroke()
  69. .foregroundColor(Color(.systemGray4))
  70. }
  71. }
  72. @ViewBuilder
  73. private var plainCheckmark: some View {
  74. if isSelected {
  75. Image(systemName: "checkmark")
  76. .resizable()
  77. .foregroundColor(.accentColor)
  78. }
  79. }
  80. }
  81. public struct DurationBasedCheckmarkListItem: View {
  82. var title: Text
  83. var titleFont: Font
  84. var description: Text
  85. @Binding var isSelected: Bool
  86. let isEnabled: Bool
  87. @Binding var duration: TimeInterval
  88. var validDurationRange: ClosedRange<TimeInterval>
  89. public init(title: Text, titleFont: Font = .headline, description: Text, isSelected: Binding<Bool>, isEnabled: Bool = true,
  90. duration: Binding<TimeInterval>, validDurationRange: ClosedRange<TimeInterval>) {
  91. self.title = title
  92. self.titleFont = titleFont
  93. self.description = description
  94. self._isSelected = isSelected
  95. self.isEnabled = isEnabled
  96. self._duration = duration
  97. self.validDurationRange = validDurationRange
  98. }
  99. public var body: some View {
  100. VStack(spacing: 0) {
  101. CheckmarkListItem(title: title, titleFont: titleFont, description: description, isSelected: $isSelected, isEnabled: isEnabled)
  102. if isSelected {
  103. DurationPicker(duration: $duration, validDurationRange: validDurationRange)
  104. .frame(height: 216)
  105. .transition(.fadeInFromTop)
  106. }
  107. }
  108. }
  109. }