Using Swift5.2.3, iOS14.4.2, XCode12.4,
Working with the .sheet
modifier in SwiftUI made me feel excited at first since it seemed like an easy and efficient way to display a modal sheet.
However, inside a real-world application it turns out that .sheet
is all but ready for integration.
Here are two bugs found:
Bug 1: The sheet does not close sporadically
Bug 2: The Picker with DefaultPickerStyle does not work when inside a sheet's SegmentPicker (See this Stackoverlow-question that I created)
Let's focus now on Bug Nr1 : "sheet does not close":
The cmd presentationMode.wrappedValue.dismiss()
is supposed to close a sheet. It works 90% of the cases. But every so often and without giving a hin on its reasons, the modal-sheet does not close.
Here is a code-excerpt:
import SwiftUI
import Firebase
struct MyView: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
VStack {
Form {
Section(header: Text("Login")) {
Button(action: {
UserDefaults.standard.set(true, forKey: AppConstants.UserDefaultKeys.justLogoutLoginPressed)
try? Auth.auth().signOut()
// supposedly should work all the time - but it only works 90% of the time.....
presentationMode.wrappedValue.dismiss()
}) {
HStack {
Text((Auth.auth().currentUser?.isAnonymous ?? true) ? "Login" : "Logout")
Spacer()
}
}
}
}
.ignoresSafeArea()
Spacer()
}
}
}
I also tried to wrap the closing call inside the main-thread:
DispatchQueue.main.async {
presentationMode.wrappedValue.dismiss()
}
But it did not help.
Any idea why SwiftUI .sheets
would not close using the presentationMode
to dismiss it ??
Here I added the way the sheet is called in the first place. Since taken out of a bigger App, I obviously only show an example here on how the sheet is called:
import SwiftUI
@main
struct TestKOS005App: App {
@StateObject var appStateService = AppStateService(appState: .startup)
var body: some Scene {
WindowGroup {
MainView()
.environmentObject(appStateService)
}
}
}
class AppStateService: ObservableObject {
@Published var appState: THAppState
var cancellableSet = Set<AnyCancellable>()
init(appState: THAppState) {
self.appState = appState
}
// ...
}
enum THAppState: Equatable {
case startup
case downloading
case caching
case waiting
case content(tagID: String, name: String)
case cleanup
}
struct MainView: View {
@EnvironmentObject var appStateService: AppStateService
@State var sheetState: THSheetSelection?
init() {
UINavigationBar.appearance().tintColor = UIColor(named: "title")
}
var body: some View {
ZStack {
NavigationView {
ZStack {
switch appStateService.appState {
case .caching:
Text("caching")
case .waiting:
Text("waiting")
case .content(_, _):
VStack {
Text("content")
Button(action: {
sheetState = .sheetType3
}, label: {
Text("Button")
})
}
default:
Text("no screen")
}
}
.sheet(item: $sheetState) { state in
switch state {
case .sheetType1:
Text("sheetType1")
case .sheetType2:
Text("sheetType2")
case .sheetType3:
MyView()
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
}
enum THSheetSelection: Hashable, Identifiable {
case sheetType1
case sheetType2
case sheetType3
var id: THSheetSelection { self }
}