The Problem
In the initial code, when tapping a date, I stored the currentDate value in selectedDate and changed the showSheet state to display a sheet. Inside the sheet, I used an if-else statement to handle whether the date was nil. However, a problem occurred where on the first tap, the date was always nil and processed through the else statement.
@State private var selectedDate: Date? = nil
@State private var showSheet = false
// Date tap handling
.onTapGesture {
selectedDate = currentDate
showSheet = true
}
// Sheet display
.sheet(isPresented: $showSheet) {
if let date = selectedDate {
// Processing for when date exists
} else {
// Processing for nil case
}
}
Searching online, I found quite a few similar situations. https://forums.developer.apple.com/forums/thread/694462
Problem Solution
Besides controlling a sheet with a simple boolean value, SwiftUI allows you to directly bind a value to be used in the sheet. The sheet opens when the bound value exists and automatically closes when it becomes nil.
1. Creating an Identifiable Structure (struct)
To use the .sheet(item:) modifier, the type must conform to the Identifiable protocol. Since the Date type doesn’t conform to this, I created a wrapper type.
struct IdentifiableDate: Identifiable {
let id = UUID()
let date: Date
}
@State private var selectedDate: IdentifiableDate? = nil
2. Modifying onTapGesture
The selected date is wrapped in IdentifiableDate before storing.
// Date tap handling
.onTapGesture {
// Wrap in IdentifiableDate
selectedDate = IdentifiableDate(date: currentDate)
}
3. Modifying sheet
Using the item modifier instead of isPresented to manage state.
// Sheet display
.sheet(item: $selectedDate) { date in
// Work with date
// Close sheet after work
Button("Close") {
selectedDate = nil
}
}
Reference: https://developer.apple.com/documentation/swiftui/view/sheet(item:ondismiss:content:)