It’s easy to miss but the way SwiftUI presented views inherit the environment has changed from iOS 13, iOS 14 and iOS 15.
Presenting a SwiftUI View
To experiment I have an app that has a minimum deployment target of iOS 13.6. Apple didn’t add the App
life cycle to SwiftUI until iOS 14 so I’m still using the older App and Scene Delegates.
I create and show my SwiftUI content view using a UIHostingController
from the scene delegate:
let rootView = ContentView()
window.rootViewController = UIHostingController(rootView: rootView)
My content view has a single button which triggers the presentation of a sheet view:
struct ContentView: View {
@State private var isShowingSheet = false
var body: some View {
Button("Present View") {
isShowingSheet = true
}
.sheet(isPresented: $isShowingSheet) {
SheetView()
}
}
}
Finally the SheetView
has some text and a button to dismiss the sheet:
struct SheetView: View {
@Environment(\.presentationMode) private var presentationMode
var body: some View {
VStack(spacing: 16) {
Text("Hello World!")
.font(.headline)
Text("This is a presented sheet.")
.font(.body)
Button("Done") {
presentationMode.wrappedValue.dismiss()
}
}
.padding()
}
}
Note: The way you dismiss presented views has changed in iOS 15. See dismissing SwiftUI views.
SwiftUI Environment Inheritance
We can experiment with the SwiftUI environment by setting the foreground color on our root content view:
let rootView = ContentView()
.foregroundColor(.purple)
The question is what foreground color does our presented sheet view inherit?
iOS 15 - Environment Is Inherited
When we run this on iOS 15 the presented sheet inherits the environment from the presenting content view. The button in our content view and the text and button in the presented sheet view inherit the purple foreground color:
iOS 14 - Environment Is Unpredictable
Repeating the experiment on iOS 14 shows some strange behaviour. On initial presentation the share sheet seems to have inherited the environment from the content view. The text appears in purple.
The problem happens if you start to dismiss the presented sheet by dragging the view downwards. At that point the text switches back to the default text color:
iOS 13 - Environment Is Not Inherited
The result for iOS 13 is more predictable. The presented sheet never inherits the foreground color from the environment of the content view:
What Does It Mean?
If you’re presenting SwiftUI views and you need to support back to iOS 14 or iOS 13 you should be explicit about setting the environment of any presented views:
var body: some View {
Button("Present View") {
isShowingSheet = true
}
.sheet(isPresented: $isShowingSheet) {
SheetView()
.foregroundColor(.purple)
}
}
It’s a pain, but until I can require iOS 15 I find it’s the only way to get consistent results on iOS 14 and iOS 13.