SwiftUI - 如何从SwiftUI访问UIHostingController

10

我有一个SwiftUI页面,它是从UIKit视图导航而来的。我想要给这个页面设置一个标题,我的做法是:

// code of UIKit view
let controller = UIHostingController(rootView: SwiftUIView())
controller.title = "title"
MyNavigationManager.present(controller)


有没有一种方式可以在SwiftUI中访问托管控制器?
这样,我就可以像这样编写代码: self.hostingController?.title = "标题"

最终我选择了一种简单的方式,在Hosting Controller中将self传递到SwiftUI的rootView.hostingController属性中。 - Leo Yang
3个回答

14
这里是一个可能的方法演示 - 使用外部配置包装类来持有对控制器的弱引用,并将其注入到SwiftUI视图中(作为替代,也可以将其设置为 ObservableObject 以与其他全局属性和逻辑相结合)。
在Xcode 12.5 / iOS 14.5下进行了测试。
class Configuration {
    weak var hostingController: UIViewController?    // << wraps reference
}

struct SwiftUIView: View {
    let config: Configuration   // << reference here

    var body: some View {
        Button("Demo") {
            self.config.hostingController?.title = "New Title"
        }
    }
}

let configuration = ExtConfiguration()
let controller = UIHostingController(rootView: SwiftUIView(config: configuration))

// injects here, because `configuration` is a reference !!
configuration.hostingController = controller

controller.title = "title"
MyNavigationManager.present(controller)

7
我选择了另一种选项 - 子类化NSHostingController,以便它将自己提供为环境变量。
struct SwiftUIView: View {
    @EnvironmentObject var host:HSHostWrapper

    var body: some View {
        Button("Dismiss") {
            host.controller?.dismiss(self)
        }
    }
}

let controller = HSHostingController(rootView: SwiftUIView())

这可以通过以下的HSHostingController实现(该控制器可在HSSwiftUI包中获得)

import Foundation
import SwiftUI


#if os(iOS) || os(tvOS)
    import UIKit
    public typealias ViewController = UIViewController
    public typealias HostingController = UIHostingController
#elseif os(OSX)
    import AppKit
    public typealias ViewController = NSViewController
    public typealias HostingController = NSHostingController
#endif

public class HSHostWrapper:ObservableObject {
    public weak var controller:ViewController?
}


/// allows root view (and children) to access the hosting controller by adding
/// @EnvironmentObject var host:HSHostWrapper
/// then e.g. host.controller?.dismiss()
public class HSHostingController<Content>:HostingController<ModifiedContent<Content,SwiftUI._EnvironmentKeyWritingModifier<HSHostWrapper?>>> where Content : View {
    
    public init(rootView:Content) {
        let container = HSHostWrapper()
        let modified = rootView.environmentObject(container) as! ModifiedContent<Content, _EnvironmentKeyWritingModifier<HSHostWrapper?>>
        super.init(rootView: modified)
        container.controller = self
    }
  
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

编辑:注意-此方法现在已在HSSwiftUI中被弃用。 请查看我后续的答案以获取更新、更简洁的方法。


2

我有一个新的解决方案。

这依赖于ViewController的扩展,它将自身作为输入提供给ViewBuilder。

我认为这非常干净。

let controller = ViewController() {
    vc in
    vc.title = "title"
    return SwiftUIView()
}
MyNavigationManager.present(controller)

这取决于HSSwiftUI包

https://swiftpackageindex.com/ConfusedVorlon/HSSwiftUI


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接