简体   繁体   中英

`SwiftUI` - `EnvironmentObject`s, `EnvironmentValues` does not propagate to modally presented `UIViewController`

SwiftUI 's EnvironmentObject s and EnvironmentValues should propagate to children if view hierarchy is set properly - this seems to be true for a case of pure SwiftUI view hierarchy. Also true for case mixing with UIKit when parent view controller literally contains its children (for example; UITabViewController , UINavigationController , ...).

But modally presented UIKit view controller on SwiftUI view hierarchy does not get the environments from its parent. Here are my setups.

// ContentView
struct ContentView: View {

    var body: some View {
        TabBarController()
            .environment(\.lineLimit, 3) // override default value of lineLimit
    }

}

// TabBarController
struct TabBarController: UIViewControllerRepresentable {

    typealias UIViewControllerType = UITabBarController

    func makeUIViewController(context: Context) -> UIViewControllerType {
        let uiViewController = UITabBarController()
        uiViewController.setViewControllers([
            UIHostingController(rootView: FooBar()),
            UIHostingController(rootView: FooBar()),
            UIHostingController(rootView: FooBar())
        ], animated: false)
        return uiViewController
    }

    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        // For test purpose, present view controller modally
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            uiViewController.present(UIHostingController(rootView: FooBar()), animated: true)
        }
    }

}

// FooBar
struct FooBar: View {

    @Environment(\.lineLimit) var lineLimit

    var body: some View {
        Text("Hello World!")
            .onAppear {
                print(lineLimit) // prints given environment value
            }
    }

}

In TabBarController implementation I set up three tabs with their root view FooBar , and it presents a modal view controller with its root view FooBar . FooBar does nothing but prints value of lineLimit from its EnvironmentValues on appear. I set lineLimit to 3 to override its default value. The tab components prints 3, whereas the presented view prints nil (which is default value).

What am I doing wrong, or is it prohibited? Weird if it is, because sheet() from SwiftUI framework does work as expected (though I cannot use this API because I need full control of UIViewController presentation to apply custom transition ).

SwiftUI's EnvironmentObjects and EnvironmentValues should propagate ONLY to SwiftUI children views . For other (trans-framework/API) cases we have to do this manually, like

struct TabBarController: UIViewControllerRepresentable {
    @Environment(\.lineLimit) var lineLimit                // << here !!

    typealias UIViewControllerType = UITabBarController

    func makeUIViewController(context: Context) -> UIViewControllerType {
        let uiViewController = UITabBarController()
        uiViewController.setViewControllers([
            UIHostingController(rootView: 
                FooBar().environment(\.lineLimit, lineLimit)),   // << here !!
            UIHostingController(rootView: FooBar()),
            UIHostingController(rootView: FooBar())
        ], animated: false)
        return uiViewController
    }

    // ... other code
}

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM