获取UIViewControllerRepresentableContext环境值?

时间:2019-06-14 16:26:55

标签: swift swiftui

好吧

所以这可能是微不足道的,但是我不确定该怎么做。

我有一个在SwiftUI视图调用时创建的UIViewController:

func makeUIViewController(context: Context) -> MyViewController

像在教程中看到的那样,在SceneDelegate中为进行调用的View提供了一个环境对象:

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

我想做的是在UIViewController逻辑中使用该环境对象(MyData())。 ViewController会根据需要在MyData的实例上进行读取/写入,据我所知,这将导致SwiftUI视图做出相应的反应,因为MyData符合 BindableObject ...

因此,在makeUIViewController调用中,我得到了UIViewControllerRepresentableContext。我可以在上下文中看到环境:

context.environment

,如果在调试期间在控制台中将其打印出来,则会看到以下内容:

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>)), .......

在打印中,我看到MyData环境对象实例:

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

我不确定如何从context.environment中给我的环境值中获取MyData。...

我试图弄清楚如何为MyData获取正确的EnvironmentKey,以便尝试访问它的下标... context.environment [myKey ...]

如何从上下文给我的环境值中取回MyData?

2 个答案:

答案 0 :(得分:0)

现在可以使用@EnvironmentObject(但不能在Xcode Preview中使用)。使用Xcode 11.1 / Swift 5.1。为了简单起见,它使用了UIViewRepresentable,但同样适用于UIViewControllerRepresentable,因为它也是SwiftUI View

这是完整的演示

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)
    }
}

答案 1 :(得分:0)

我遇到了同样的问题,Apple 的优秀教程 https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit 对此进行了解答。

您所做的基本上是将绑定传递到您的 ViewController。


import SwiftUI
import UIKit

struct PageViewController<Page: View>: UIViewControllerRepresentable {
    var pages: [Page]
    @Binding var currentPage: Int
    ...

然后像这样传递绑定:


import SwiftUI

struct PageView<Page: View>: View {
    var pages: [Page]
    @State private var currentPage = 0

    var body: some View {
        ZStack(alignment: .bottomTrailing) {
            PageViewController(pages: pages, currentPage: $currentPage)
            //                               Here is the binding -^
            PageControl(numberOfPages: pages.count, currentPage: $currentPage)
                .frame(width: CGFloat(pages.count * 18))
                .padding(.trailing)
        }
    }
}

这当然也适用于 @EnvironmentObject 而不是 @State