Sheets don’t inherit the environment – Ole Begemann

[ad_1]

This behavior has been fixed. As of Xcode 12.0 (the iOS 14.0/macOS 11.0 SDKs), sheets do inherit their environment.


Unlike other views, modal sheets in SwiftUI do not inherit the environment from their parent.

The environment is SwiftUI’s way to pass data implicitly to child views. Among other things, the environment contains app- or system-wide preferences, such as the user’s locale or the current color scheme. Essentially, you can think of the environment as a large, heterogeneous dictionary that gets passed implicitly to every view.

Many built-in SwiftUI views take the environment into account when they draw themselves. We can take advantage of this to override a setting for all child views with a single line of code. Consider this example:



The single .font modifier overrides the font for all child views.
VStack(spacing: 8) {
  Text("Line 1")
  HStack {
    Text("Line 2")
    VStack {
      Text("Line 2a")
      Text("Line 2b")
    }
  }.font(.title)
  Text("Line 3")
}

The .font(.title) modifier on the HStack mutates the corresponding value in the current environment, which then gets passed to the stack’s child views. And because Text grabs its font from the environment, all text views in this section of the view tree are rendered with a larger font size. Note that the modified environment also applies to indirect children of the HStack.

The following example creates a root view whose locale and dynamic type size have been overridden in the environment. The view displays a formatted date and the current dynamic type size setting. Here’s the code for the root view:

struct RootView: View {
  var body: some View {
    RootViewContent()
      .environment(\.sizeCategory, .accessibilityMedium)
      .environment(\.locale, Locale(identifier: "ja_JP"))
  }
}

struct RootViewContent: View {
  @State var isPresentingSheet = false
  @Environment(\.sizeCategory) var sizeCategory

  var body: some View {
    VStack(spacing: 16) {
      Text("Root View").font(.title)
      Text("\(Date(), formatter: dateFormatter)")
      Text("Size category: \(String(describing: sizeCategory))")
      Button("Open Sheet") {
        self.isPresentingSheet = true
      }
    }
    .sheet(isPresented: self.$isPresentingSheet) {
      ChildView()
    }
  }
}

Tapping the button in the root view sets a state variable that triggers the presentation of a modal sheet, using the .sheet modifier:

    // …
    .sheet(isPresented: self.$isPresentingSheet) {
      ChildView()
    }
    // …

The view that gets presented displays the same data as the root view, without modifying the environment in any way:

struct ChildView: View {
  @Environment(\.sizeCategory) var sizeCategory

  var body: some View {
    VStack(spacing: 16) {
      Text("Child View").font(.title)
      Text("\(Date(), formatter: dateFormatter)")
      Text("Size category: \(String(describing: sizeCategory))")
    }
  }
}

I’d expect that the presented view inherits the environment from the presenting view (some modifications notwithstanding since the presentation mode is also stored in the environment), but that’s clearly not the case; while the root view correctly uses the Japanese locale and a very large dynamic type setting, the child view goes back to the system locale and text size:


Screenshots of the presenting view displaying large text and a Japanese-formatted date, and the presented view displaying normal text and an English-formatted date.
The presenting view didn’t pass its environment to the presented view.

I’m not sure if this is intentional or a bug. (Update: It’s a bug.) I guess if you see sheets as independent entities that should not be affected by their presenting view, it makes sense for some environment values not to get propagated. Examples of this kind might include:

For other settings, such as locale and dynamic type size, not propagating seems the wrong choice to me. It looks like there is no single option that works for everything, though. And making this a configurable behavior that every EnvironmentKey can decide for itself could also be confusing.

If you want to propagate an environment value to a sheet, you must do so manually. In our example, the code would look like this:

    // …
    .sheet(isPresented: self.$isPresentingSheet) {
      ChildView()
        .environment(\.sizeCategory, self.sizeCategory)      
        .environment(\.locale, self.locale)
    }
    // …

(This assumes that you also added a property @Environment(\.locale) var locale to the root view in order to access the current locale inside the environment.)



[ad_2]

Source link

Leave a Reply

Your email address will not be published.