@@ -18,7 +18,8 @@ struct MeterView: View {
|
||
| 18 | 18 |
@State var recordingViewVisibility: Bool = false |
| 19 | 19 |
@State var measurementsViewVisibility: Bool = false |
| 20 | 20 |
private var myBounds: CGRect { UIScreen.main.bounds }
|
| 21 |
- private let actionButtonSpacing: CGFloat = 12 |
|
| 21 |
+ private let actionStripPadding: CGFloat = 10 |
|
| 22 |
+ private let actionDividerWidth: CGFloat = 1 |
|
| 22 | 23 |
private let actionButtonMaxWidth: CGFloat = 156 |
| 23 | 24 |
private let actionButtonMinWidth: CGFloat = 88 |
| 24 | 25 |
private let actionButtonHeight: CGFloat = 108 |
@@ -26,8 +27,7 @@ struct MeterView: View {
|
||
| 26 | 27 |
var body: some View {
|
| 27 | 28 |
ScrollView {
|
| 28 | 29 |
VStack(alignment: .leading, spacing: 16) {
|
| 29 |
- headerCard |
|
| 30 |
- connectionControlButton() |
|
| 30 |
+ connectionCard |
|
| 31 | 31 |
|
| 32 | 32 |
if meter.operationalState == .dataIsAvailable {
|
| 33 | 33 |
actionGrid |
@@ -82,8 +82,8 @@ struct MeterView: View {
|
||
| 82 | 82 |
}) |
| 83 | 83 |
} |
| 84 | 84 |
|
| 85 |
- private var headerCard: some View {
|
|
| 86 |
- VStack(alignment: .leading, spacing: 14) {
|
|
| 85 |
+ private var connectionCard: some View {
|
|
| 86 |
+ VStack(alignment: .leading, spacing: 18) {
|
|
| 87 | 87 |
HStack(alignment: .top) {
|
| 88 | 88 |
VStack(alignment: .leading, spacing: 6) {
|
| 89 | 89 |
Text(meter.name) |
@@ -106,6 +106,8 @@ struct MeterView: View {
|
||
| 106 | 106 |
} |
| 107 | 107 |
} |
| 108 | 108 |
} |
| 109 |
+ |
|
| 110 |
+ connectionActionArea |
|
| 109 | 111 |
} |
| 110 | 112 |
.padding(20) |
| 111 | 113 |
.meterCard(tint: meter.color, fillOpacity: 0.22, strokeOpacity: 0.24) |
@@ -114,10 +116,11 @@ struct MeterView: View {
|
||
| 114 | 116 |
private var actionGrid: some View {
|
| 115 | 117 |
GeometryReader { proxy in
|
| 116 | 118 |
let buttonWidth = actionButtonWidth(for: proxy.size.width) |
| 119 |
+ let stripWidth = actionStripWidth(for: buttonWidth) |
|
| 117 | 120 |
|
| 118 | 121 |
HStack {
|
| 119 | 122 |
Spacer(minLength: 0) |
| 120 |
- HStack(spacing: actionButtonSpacing) {
|
|
| 123 |
+ HStack(spacing: 0) {
|
|
| 121 | 124 |
meterSheetButton(icon: "square.grid.2x2.fill", title: meter.dataGroupsTitle, tint: .teal, width: buttonWidth) {
|
| 122 | 125 |
dataGroupsViewVisibility.toggle() |
| 123 | 126 |
} |
@@ -127,6 +130,7 @@ struct MeterView: View {
|
||
| 127 | 130 |
} |
| 128 | 131 |
|
| 129 | 132 |
if meter.supportsRecordingView {
|
| 133 |
+ actionStripDivider |
|
| 130 | 134 |
meterSheetButton(icon: "gauge.with.dots.needle.50percent", title: "Charge Record", tint: .pink, width: buttonWidth) {
|
| 131 | 135 |
recordingViewVisibility.toggle() |
| 132 | 136 |
} |
@@ -136,6 +140,7 @@ struct MeterView: View {
|
||
| 136 | 140 |
} |
| 137 | 141 |
} |
| 138 | 142 |
|
| 143 |
+ actionStripDivider |
|
| 139 | 144 |
meterSheetButton(icon: "clock.arrow.circlepath", title: "App History", tint: .blue, width: buttonWidth) {
|
| 140 | 145 |
measurementsViewVisibility.toggle() |
| 141 | 146 |
} |
@@ -144,15 +149,18 @@ struct MeterView: View {
|
||
| 144 | 149 |
.environmentObject(meter.measurements) |
| 145 | 150 |
} |
| 146 | 151 |
} |
| 152 |
+ .padding(actionStripPadding) |
|
| 153 |
+ .frame(width: stripWidth) |
|
| 154 |
+ .meterCard(tint: Color.secondary, fillOpacity: 0.10, strokeOpacity: 0.16) |
|
| 147 | 155 |
Spacer(minLength: 0) |
| 148 | 156 |
} |
| 149 | 157 |
} |
| 150 |
- .frame(height: actionButtonHeight) |
|
| 158 |
+ .frame(height: actionButtonHeight + (actionStripPadding * 2)) |
|
| 151 | 159 |
} |
| 152 | 160 |
|
| 153 |
- fileprivate func connectionControlButton() -> some View {
|
|
| 161 |
+ private var connectionActionArea: some View {
|
|
| 154 | 162 |
let connected = meter.operationalState >= .peripheralConnectionPending |
| 155 |
- let tint = connected ? Color.red : Color.green |
|
| 163 |
+ let tint = connected ? disconnectActionTint : connectActionTint |
|
| 156 | 164 |
|
| 157 | 165 |
return Group {
|
| 158 | 166 |
if meter.operationalState == .notPresent {
|
@@ -164,7 +172,7 @@ struct MeterView: View {
|
||
| 164 | 172 |
Spacer() |
| 165 | 173 |
} |
| 166 | 174 |
.padding(16) |
| 167 |
- .meterCard(tint: .orange, fillOpacity: 0.18, strokeOpacity: 0.24) |
|
| 175 |
+ .meterCard(tint: .orange, fillOpacity: 0.14, strokeOpacity: 0.18) |
|
| 168 | 176 |
} else {
|
| 169 | 177 |
Button(action: {
|
| 170 | 178 |
if meter.operationalState < .peripheralConnectionPending {
|
@@ -173,20 +181,20 @@ struct MeterView: View {
|
||
| 173 | 181 |
meter.disconnect() |
| 174 | 182 |
} |
| 175 | 183 |
}) {
|
| 176 |
- HStack {
|
|
| 184 |
+ HStack(spacing: 12) {
|
|
| 177 | 185 |
Image(systemName: connected ? "xmark.circle.fill" : "bolt.horizontal.circle.fill") |
| 186 |
+ .foregroundColor(tint) |
|
| 187 |
+ .frame(width: 30, height: 30) |
|
| 188 |
+ .background(Circle().fill(tint.opacity(0.12))) |
|
| 178 | 189 |
Text(connected ? "Disconnect" : "Connect") |
| 179 | 190 |
.fontWeight(.semibold) |
| 191 |
+ .foregroundColor(.primary) |
|
| 180 | 192 |
Spacer() |
| 181 |
- Text(statusText) |
|
| 182 |
- .font(.footnote.weight(.medium)) |
|
| 183 |
- .foregroundColor(.secondary) |
|
| 184 | 193 |
} |
| 185 | 194 |
.padding(.horizontal, 18) |
| 186 |
- .padding(.vertical, 16) |
|
| 195 |
+ .padding(.vertical, 14) |
|
| 187 | 196 |
.frame(maxWidth: .infinity) |
| 188 |
- .foregroundColor(tint) |
|
| 189 |
- .meterCard(tint: tint, fillOpacity: 0.18, strokeOpacity: 0.24) |
|
| 197 |
+ .meterCard(tint: tint, fillOpacity: 0.14, strokeOpacity: 0.20) |
|
| 190 | 198 |
} |
| 191 | 199 |
.buttonStyle(.plain) |
| 192 | 200 |
} |
@@ -208,7 +216,7 @@ struct MeterView: View {
|
||
| 208 | 216 |
} |
| 209 | 217 |
.foregroundColor(tint) |
| 210 | 218 |
.frame(width: width, height: actionButtonHeight) |
| 211 |
- .meterCard(tint: tint, fillOpacity: 0.18, strokeOpacity: 0.22) |
|
| 219 |
+ .contentShape(Rectangle()) |
|
| 212 | 220 |
} |
| 213 | 221 |
.buttonStyle(.plain) |
| 214 | 222 |
} |
@@ -218,11 +226,31 @@ struct MeterView: View {
|
||
| 218 | 226 |
} |
| 219 | 227 |
|
| 220 | 228 |
private func actionButtonWidth(for availableWidth: CGFloat) -> CGFloat {
|
| 221 |
- let spacingWidth = actionButtonSpacing * max(visibleActionButtonCount - 1, 0) |
|
| 222 |
- let fittedWidth = floor((availableWidth - spacingWidth) / visibleActionButtonCount) |
|
| 229 |
+ let dividerWidth = actionDividerWidth * max(visibleActionButtonCount - 1, 0) |
|
| 230 |
+ let contentWidth = availableWidth - (actionStripPadding * 2) - dividerWidth |
|
| 231 |
+ let fittedWidth = floor(contentWidth / visibleActionButtonCount) |
|
| 223 | 232 |
return min(actionButtonMaxWidth, max(actionButtonMinWidth, fittedWidth)) |
| 224 | 233 |
} |
| 225 | 234 |
|
| 235 |
+ private func actionStripWidth(for buttonWidth: CGFloat) -> CGFloat {
|
|
| 236 |
+ let dividerWidth = actionDividerWidth * max(visibleActionButtonCount - 1, 0) |
|
| 237 |
+ return (buttonWidth * visibleActionButtonCount) + dividerWidth + (actionStripPadding * 2) |
|
| 238 |
+ } |
|
| 239 |
+ |
|
| 240 |
+ private var actionStripDivider: some View {
|
|
| 241 |
+ Rectangle() |
|
| 242 |
+ .fill(Color.secondary.opacity(0.16)) |
|
| 243 |
+ .frame(width: actionDividerWidth, height: actionButtonHeight - 24) |
|
| 244 |
+ } |
|
| 245 |
+ |
|
| 246 |
+ private var connectActionTint: Color {
|
|
| 247 |
+ Color(red: 0.20, green: 0.46, blue: 0.43) |
|
| 248 |
+ } |
|
| 249 |
+ |
|
| 250 |
+ private var disconnectActionTint: Color {
|
|
| 251 |
+ Color(red: 0.66, green: 0.39, blue: 0.35) |
|
| 252 |
+ } |
|
| 253 |
+ |
|
| 226 | 254 |
private var statusText: String {
|
| 227 | 255 |
switch meter.operationalState {
|
| 228 | 256 |
case .notPresent: |