parent
e4758a3fea
commit
c3e4d40116
@ -0,0 +1,127 @@ |
|||||||
|
// |
||||||
|
// TimePickerView.swift |
||||||
|
// LeCountdown |
||||||
|
// |
||||||
|
// Created by Laurent Morvillier on 17/05/2023. |
||||||
|
// |
||||||
|
|
||||||
|
import SwiftUI |
||||||
|
|
||||||
|
struct MultiComponentPicker<Tag: Hashable>: View { |
||||||
|
let columns: [Column] |
||||||
|
var selections: [Binding<Tag>] |
||||||
|
|
||||||
|
init?(columns: [Column], selections: [Binding<Tag>]) { |
||||||
|
guard !columns.isEmpty && columns.count == selections.count else { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
self.columns = columns |
||||||
|
self.selections = selections |
||||||
|
} |
||||||
|
|
||||||
|
var body: some View { |
||||||
|
GeometryReader { geometry in |
||||||
|
HStack(spacing: 0) { |
||||||
|
ForEach(0..<columns.count, id: \.self) { index in |
||||||
|
let column = columns[index] |
||||||
|
ZStack(alignment: Alignment.init(horizontal: .customCenter, vertical: .center)) { |
||||||
|
if (!column.label.isEmpty && !column.options.isEmpty) { |
||||||
|
HStack { |
||||||
|
Text(verbatim: String("ld")) |
||||||
|
.foregroundColor(.clear) |
||||||
|
.alignmentGuide(.customCenter) { $0[HorizontalAlignment.center] } |
||||||
|
Text(column.label).font(Font.footnote) |
||||||
|
} |
||||||
|
} |
||||||
|
Picker(column.label, selection: selections[index]) { |
||||||
|
ForEach(column.options, id: \.tag) { option in |
||||||
|
Text(verbatim: option.text).tag(option.tag) |
||||||
|
} |
||||||
|
} |
||||||
|
.pickerStyle(WheelPickerStyle()) |
||||||
|
.frame(width: geometry.size.width / CGFloat(columns.count), height: geometry.size.height) |
||||||
|
.clipped() |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
extension MultiComponentPicker { |
||||||
|
struct Column { |
||||||
|
struct Option { |
||||||
|
var text: String |
||||||
|
var tag: Tag |
||||||
|
} |
||||||
|
|
||||||
|
var label: String |
||||||
|
var options: [Option] |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private extension HorizontalAlignment { |
||||||
|
enum CustomCenter: AlignmentID { |
||||||
|
static func defaultValue(in context: ViewDimensions) -> CGFloat { context[HorizontalAlignment.center] } |
||||||
|
} |
||||||
|
|
||||||
|
static let customCenter = Self(CustomCenter.self) |
||||||
|
} |
||||||
|
|
||||||
|
struct TimePickerView: View { |
||||||
|
|
||||||
|
@Binding var duration: TimeInterval |
||||||
|
|
||||||
|
@State var hours = 0 |
||||||
|
@State var minutes = 0 |
||||||
|
@State var seconds = 0 |
||||||
|
|
||||||
|
var hoursBinding: Binding<Int> { Binding( |
||||||
|
get: { self.hours }, |
||||||
|
set: { |
||||||
|
self.hours = $0 |
||||||
|
self._computeDuration() |
||||||
|
} |
||||||
|
) } |
||||||
|
|
||||||
|
var minutesBinding: Binding<Int> { Binding( |
||||||
|
get: { self.minutes }, |
||||||
|
set: { |
||||||
|
self.minutes = $0 |
||||||
|
self._computeDuration() |
||||||
|
} |
||||||
|
) } |
||||||
|
|
||||||
|
var secondsBinding: Binding<Int> { Binding( |
||||||
|
get: { self.seconds }, |
||||||
|
set: { |
||||||
|
self.seconds = $0 |
||||||
|
self._computeDuration() |
||||||
|
} |
||||||
|
) } |
||||||
|
|
||||||
|
var columns = [ |
||||||
|
MultiComponentPicker.Column(label: "h", options: Array(0..<24).map { MultiComponentPicker.Column.Option(text: "\($0)", tag: $0) }), |
||||||
|
MultiComponentPicker.Column(label: "min", options: Array(0..<60).map { MultiComponentPicker.Column.Option(text: "\($0)", tag: $0) }), |
||||||
|
MultiComponentPicker.Column(label: "sec", options: Array(0..<60).map { MultiComponentPicker.Column.Option(text: "\($0)", tag: $0) }), |
||||||
|
] |
||||||
|
|
||||||
|
fileprivate func _computeDuration() { |
||||||
|
self.duration = Double(hours) * 3600.0 + Double(minutes) * 60.0 + Double(seconds) |
||||||
|
Logger.log("duration = \(self.duration)") |
||||||
|
} |
||||||
|
|
||||||
|
var body: some View { |
||||||
|
MultiComponentPicker(columns: columns, selections: [hoursBinding, minutesBinding, secondsBinding]) |
||||||
|
.frame(height: 200) |
||||||
|
.previewLayout(.sizeThatFits) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
struct MultiComponentPicker_Previews: PreviewProvider { |
||||||
|
static var previews: some View { |
||||||
|
TimePickerView(duration: .constant(0.0)).padding(48.0) |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue