[英]How to set @EnvironmentObject in an existing Storyboard based project?
我有一个用Storyboard和UIKit构建的 iOS 项目。 现在我想使用SwiftUI开发新屏幕。 我向现有的Storyboard添加了一个托管视图 Controller,并用它来显示我新创建的 SwiftUI视图。
但是我不知道如何创建一个可以在整个应用程序的任何地方使用的@Environm.netObject 。 我应该能够在我的任何基于 UIKit 的 ViewController 以及我的SwiftUI视图中访问/设置它。
这可能吗? 如果是这样怎么办? 在纯SwiftUI应用程序中,我们设置环境 object,如下所示,
@main
struct myApp: App {
@StateObject var item = Item()
var body: some Scene {
WindowGroup {
MainView()
.environmentObject(item)
}
}
}
但就我而言,没有像这样的 function,因为它是一个现有的 iOS 项目,带有AppDelegate和SceneDelegate 。 而初始视图controller标记在Storyboard中。如何设置并在应用程序的任何位置访问object?
.environmentObject 修饰符将视图的类型从 ItemDetailView 更改为其他内容。 强制转换它会导致错误。 相反,请尝试将其包装到 AnyView 中。
class OrderObservable: ObservableObject {
@Published var order: String = "Hello"
}
struct ItemDetailView: View {
@EnvironmentObject var orderObservable: OrderObservable
var body: some View {
EmptyView()
.onAppear(perform: {
print(orderObservable.order)
})
}
}
class ItemDetailViewHostingController: UIHostingController<AnyView> {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
required init?(coder: NSCoder) {
super.init(coder: coder,rootView: AnyView(ItemDetailView().environmentObject(OrderObservable())))
}
}
这对我有用。 这是你需要的吗?
编辑:好的,所以我通过 View 设置了 ViewController 中的属性。 它不像使用属性包装器或视图修饰符那么容易,但它确实有效。 我试了一下。 请让我知道这是否满足您的要求。 此外,我必须摆脱 HostingController 子类。
class ViewController: UIViewController {
var orderObservable = OrderObservable()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let myVC = (segue.destination as? MyViewController) else { return }
myVC.orderObservable = orderObservable
}
}
class MyViewController: UIViewController {
var orderObservable: OrderObservable!
var anycancellables = Set<AnyCancellable>()
@IBAction @objc func buttonSegueToHostingVC() {
let detailView = ItemDetailView().environmentObject(orderObservable)
present(UIHostingController(rootView: detailView), animated: true)
orderObservable.$order.sink { newVal in
print(newVal)
}
.store(in: &anycancellables)
}
}
class OrderObservable: ObservableObject {
@Published var order: String = "Hello"
init() {
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
self.order = "World"
}
}
}
struct ItemDetailView: View {
@EnvironmentObject var orderObservable: OrderObservable
var body: some View {
Text("\(orderObservable.order)")
}
}
Basically I'm creating the observable object in the ViewController class, passing it to the MyViewController class and finally create a hosting controller with the ItemDetailView and setting it's environmentObject and presenting it.
这是我对解决这个问题的看法。 我的应用程序针对 iOS 14 或更高版本:
我有一个 Main.storyboard 文件,其中一个视图 controller 场景设置为初始视图 controller 和自定义 class ViewController
。 这是自定义 class 实现:
import UIKit
class ViewController: UIViewController {
@IBOutlet var label: UILabel!
}
在 SwiftUI 应用程序生命周期中使用此 class 并使其与@EnvironmentObject
实例(在本例中我们称其为主题管理器)做出反应和交互。
我将定义一个ThemeManager
observable object 和Theme
published 属性,如下所示:
import SwiftUI
class ThemeManager: ObservableObject {
@Published var theme = Theme.purple
}
struct Theme {
let labelColor: Color
}
extension Theme {
static let purple = Theme(labelColor: .purple)
static let green = Theme(labelColor: .green)
}
extension Theme: Equatable {}
接下来,我创建了一个ViewControllerRepresentation
以便能够在 SwiftUI 中使用ViewController
:
import SwiftUI
struct ViewControllerRepresentation: UIViewControllerRepresentable {
@EnvironmentObject var themeManager: ThemeManager
// Use this function to pass the @EnvironmentObject to the view controller
// so that you can change its properties from inside the view controller scope.
func makeUIViewController(context: Context) -> ViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateInitialViewController { coder in
ViewController(themeManager: themeManager, coder: coder)
}
return viewController!
}
// Use this function to update the view controller when the @EnvironmentObject changes.
// In this case I modify the label color based on the themeManager.
func updateUIViewController(_ uiViewController: ViewController, context: Context) {
uiViewController.label.textColor = UIColor(themeManager.theme.labelColor)
}
}
然后我更新了ViewController
以接受themeManager
实例:
import UIKit
class ViewController: UIViewController {
@IBOutlet var label: UILabel!
let themeManager: ThemeManager
init?(themeManager: ThemeManager, coder: NSCoder) {
self.themeManager = themeManager
super.init(coder: coder)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@IBAction func toggleTheme(_ sender: UIButton) {
if themeManager.theme == .purple {
themeManager.theme = .green
} else {
themeManager.theme = .purple
}
}
}
现在,最后要做的是创建主题管理器的实例并将其作为环境 object 传递给视图 controller 表示:
import SwiftUI
@main
struct ThemeEnvironmentApp: App {
@StateObject private var themeManager = ThemeManager()
var body: some Scene {
WindowGroup {
ViewControllerRepresentation()
.environmentObject(themeManager)
}
}
}
运行该应用程序会显示我们的视图 controller 以及一个 label 和一个按钮。 点击按钮会触发IBAction
,这会更改themeManager.theme
,这会触发对表示的updateUIViewController(_:, context:)
的调用:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.