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