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:)