I have a view which presents in a popover if the size class is regular, and as a sheet, if size class is compact (standard system presentation behavior). In this view, I have a navigation view. I'd like to only display a “Done” toolbar item if the view is presented as a sheet, but not if presented in a popover.
In UIKit, this is very easy to accomplish, but in SwiftUI, I don't see any environment value that describes the presentation of the view.
I saw suggestions to look at the horizontalSizeClass
environment value, but that doesn't work, because the view is presented with a compact size class inside the popover.
Here is my view:
struct SettingsView: View {
fileprivate let internalSettings = SettingsViewInternal()
@Environment(\.dismiss) var dismiss
@Environment(\.horizontalSizeClass) var sizeClass
var body: some View {
NavigationStack {
internalSettings
.navigationTitle("Settings")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Reset") {
internalSettings.reset()
}
}
//This gets called twice, first time with a standard size class,
//then with compact size class when the popover presents its content.
if sizeClass == .compact {
ToolbarItem(placement: .confirmationAction) {
Button("Done") {
self.dismiss()
}
}
}
}
}
}
}
I present the popover like this:
Button {
settingsPresented = true
} label: {
Image("gears")
}
.popover(isPresented: $settingsPresented) {
SettingsView()
}
In such scenario a possible solution is to track global horizontalSizeClass
instead of local (which by Apple's doc depends on many things), and inject it into some (say AppState
) environment object that can be transferred into any presentation.
So it could look like
struct ContentView: View {
@StateObject private var appState = AppState()
@Environment(\.horizontalSizeClass) var sizeClass
var body: some View {
SomeTopView()
.onAppear {
appState.sizeClass = sizeClass
}
.onChange(of: sizeClass) {
appState.sizeClass = $0
}
.environmentObject(appState) // << for all view hierarchy !!
}
}
// ...
Button {
settingsPresented = true
} label: {
Image("gears")
}
.popover(isPresented: $settingsPresented) {
SettingsView()
.environmentObject(appState) // << should be injected explicitly !!
}
// ...
struct SettingsView: View {
@EnvironmentObject var appState: AppState
// ...
if appState.sizeClass == .compact {
ToolbarItem(placement: .confirmationAction) {
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.