I was trying to have a date picker show up conditionally; it seems to work for iOS, but macOS (13, haven't tried it on 14 yet) it ... doesn't. In particular, if I select "custom" in this code, and then click on a date part, it brings up the graphical picker, and I can select a date, but then... it doesn't go away. Clearly I am doing something wrong, but is it clear to anyone who isn't me what that is?
import SwiftUI
extension View {
/// Hide or show the view based on a boolean value.
///
/// Example for visibility:
///
/// Text("Label")
/// .isHidden(true)
///
/// Example for complete removal:
///
/// Text("Label")
/// .isHidden(true, remove: true)
///
/// - Parameters:
/// - hidden: Set to `false` to show the view. Set to `true` to hide the view.
/// - remove: Boolean value indicating whether or not to remove the view.
@ViewBuilder func isHidden(_ hidden: Bool, remove: Bool = false, disable: Bool = false) -> some View {
if hidden {
if !remove {
self.hidden()
.disabled(disable)
}
} else {
self
}
}
}
enum ExpireType: CustomStringConvertible, Hashable, CaseIterable {
case never
case oneWeek
case twoWeek
case oneMonth
case sixMonth
case custom
func expires(given date: Date) -> Date? {
let calendar = Calendar.current
switch self {
case .never:
return nil
case .custom:
return nil
case .oneWeek:
return calendar.date(byAdding: .weekOfYear, value: 1, to: Date())
case .twoWeek:
return calendar.date(byAdding: .weekOfYear, value: 2, to: Date())
case .oneMonth:
return calendar.date(byAdding:.month, value: 1, to: Date())
case .sixMonth:
return calendar.date(byAdding: .month, value: 6, to: Date())
}
}
var description: String {
switch self {
case .never: return "Never"
case .custom:
return "Custom"
case .oneWeek:
return "One week"
case .twoWeek:
return "Two weeks"
case .oneMonth:
return "One month"
case .sixMonth:
return "Six months"
}
}
}
struct ExpireDatePicker: View {
@State var expires = Date()
@State var expireType = ExpireType.never
@State var didChange = false
@State var dateString = ""
@State var showDatePicker = false
private func updateText() {
if self.expireType == .never {
self.dateString = ""
} else if self.expireType == .custom {
self.dateString = self.expires.formatted(.dateTime.day().month().year())
} else {
self.dateString = self.expireType.expires(given: Date())!.formatted(.dateTime.day().month().year())
}
}
/*
* For the expire date, we want to let
* the user pick one of the predfined dates,
* or a custom date.
*/
var body: some View {
VStack(alignment: .trailing) {
let _ = print("showDatePicker \(self.showDatePicker)")
Picker("Expiration date", selection: self.$expireType) {
ForEach(ExpireType.allCases, id: \.self) { et in
Text(String(describing: et))
.tag(et)
}
}
ZStack(alignment: .trailing) {
Text(dateString)
.fontWeight(.ultraLight)
.isHidden(self.showDatePicker, disable: true)
/*
* This does not work well.
* I can't get it to disappear,
* or relinquish control
*/
DatePicker("", selection: self.$expires, displayedComponents: .date)
.datePickerStyle(.compact)
.isHidden(!self.showDatePicker, disable: true)
}
}
.onChange(of: self.expireType) { to in
self.showDatePicker = (to == .custom)
self.updateText()
}
.onChange(of: self.expires) { to in
print("expires changed to \(self.expires)")
self.showDatePicker = false
}
}
}