【问题标题】:Get a UIViewControllerRepresentableContext environment value?获取 UIViewControllerRepresentableContext 环境值?
【发布时间】:2019-06-14 16:26:55
【问题描述】:

好的,

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

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

func makeUIViewController(context: Context) -> MyViewController

进行该调用的视图在 SceneDelegate 中被赋予了一个环境对象,就像我们在教程中看到的那样:

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 environmentObject 实例:

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

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

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

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

【问题讨论】:

  • UIViewControllerRepresentable 仍然是一个View,所以我尝试在其中使用@EnvironmentObject,并确保超级视图传递了一个环境对象。它被传递给视图控制器,但是如果您访问它,应用程序会崩溃,因为您使用的是主体外部的环境对象。我认为最好通过Bindings,除非有人找到更好的方法
  • 有趣。我还想知道这种方法是好是坏等等......然后我想为什么我不能在我的 UIViewController 中使用 MyData 是传递给我的上下文在环境值中有那个......仍然试图得到这一切在我的脑海中解决了。
  • 现在开发良好实践还为时过早,因为框架缺乏项目和适当的文档。目前,它主要是......“解决方法”。我在关于UIVIewRepresentable 的回答中成功使用了Bindings,这似乎是一个不错的选择:请查看stackoverflow.com/a/56564377/2890168stackoverflow.com/a/56568715/2890168 以查看一些示例。
  • 我正在尝试对 UIViewRepresentable 做同样的事情。从 beta 3 开始,我已经尝试了你所拥有的一切,但没有成功。我通过初始化程序注入所需的数据来解决它。工作正常,但直接从 context.environment 中得到它会更好。也许在 beta 4 中?

标签: swift swiftui


【解决方案1】:

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

这是完整的演示

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

【讨论】:

    【解决方案2】:

    我有同样的问题,苹果优秀教程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

    【讨论】:

      猜你喜欢
      • 2013-02-02
      • 2021-03-17
      • 2013-08-10
      • 2016-03-19
      • 2013-11-01
      • 1970-01-01
      • 2012-05-14
      • 2011-06-03
      • 1970-01-01
      相关资源
      最近更新 更多