Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get a UIViewControllerRepresentableContext environment value?

Tags:

swift

swiftui

Ok,

so this might be trivial, but I am not sure how to go about this.

I have a UIViewController that gets created when the SwiftUI view calls:

func makeUIViewController(context: Context) -> MyViewController

The View that makes that call was given an environment object in the SceneDelegate like we have seen in the tutorials:

window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(MyData()))

What I am trying to do is to use that environment object (MyData()) within my UIViewController logic. The ViewController would read/write on MyData's instance as needed, and from what I understand that should cause the SwiftUI view to react accordingly since MyData conforms to BindableObject...

So in the makeUIViewController call I get the UIViewControllerRepresentableContext. I can see the environment in the context:

context.environment

and if I print it in the console during debug I see this:

context.environment: [EnvironmentPropertyKey<PreferenceBridgeKey> = Value(value: Optional(SwiftUI.PreferenceBridge)), EnvironmentPropertyKey<FontKey> = Optional(SwiftUI.Font(provider: SwiftUI.(unknown context at $1c652cbec).FontBox<SwiftUI.Font.(unknown context at $1c656e2cc).TextStyleProvider>)), .......

In the print I see the MyData environmentObject instance:

EnvironmentPropertyKey<StoreKey<MyData>> = Optional(MyApp.MyData), ...

I am not sure how to get MyData out of the environment values given to me in the context.environment....

I have tried to figure out how to get the proper EnvironmentKey for MyData so I could try access it view subscript ... context.environment[myKey...]

How can I get MyData back from the environment values given to me by the context?

like image 416
zumzum Avatar asked Jun 14 '19 16:06

zumzum


1 Answers

Using @EnvironmentObject now works (but not in Xcode Preview). Used Xcode 11.1/Swift 5.1. For simplicity it was used UIViewRepresentable, but the same should work for UIViewControllerRepresentable, because it is also SwiftUI View

Here is complete demo

Change @EnvironmentObject from UIButton

import SwiftUI
import Combine
import UIKit

class AppState: ObservableObject {
    @Published var simpleFlag = false
}

struct CustomUIView: UIViewRepresentable {
    typealias UIViewType = UIButton

    @EnvironmentObject var settings: AppState

    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(type: UIButton.ButtonType.roundedRect)
        button.setTitle("Tap UIButton", for: .normal)
        button.actionHandler(controlEvents: UIControl.Event.touchUpInside) {
            self.settings.simpleFlag.toggle()
        }
        return button
    }

    func updateUIView(_ uiView: UIButton, context: UIViewRepresentableContext<CustomUIView>) {
    }

}

struct ContentView: View {
    @ObservedObject var settings: AppState = AppState()

    var body: some View {
        VStack(alignment: .center) {
            Spacer()
            CustomUIView()
                .environmentObject(self.settings)
                .frame(width: 100, height: 40)
                .border(Color.blue)
            Spacer()
            if self.settings.simpleFlag {
                Text("Activated").padding().background(Color.red)
            }
            Button(action: {
                self.settings.simpleFlag.toggle()
            }) {
                Text("SwiftUI Button")
            }
            .padding()
            .border(Color.blue)
        }
        .edgesIgnoringSafeArea(.all)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environmentObject(AppState())
    }
}

/// Just utility below
extension UIButton {
    private func actionHandler(action:(() -> Void)? = nil) {
        struct __ { static var action :(() -> Void)? }
        if action != nil { __.action = action }
        else { __.action?() }
    }
    @objc private func triggerActionHandler() {
        self.actionHandler()
    }
    func actionHandler(controlEvents control :UIControl.Event, for action:@escaping () -> Void) {
        self.actionHandler(action: action)
        self.addTarget(self, action: #selector(triggerActionHandler), for: control)
    }
}
like image 192
Asperi Avatar answered Sep 18 '22 20:09

Asperi