建立不可见的SwiftUI到UIKit到SwiftUI的桥梁

时间:2019-08-19 19:30:36

标签: ios swift uiviewcontroller swiftui

我发现SwiftUI方面特别缺少交互方面的某些功能。因此,我试图添加一个介于两个UIView之间的中间“不可见” UIViewController。

TL; DR:请参见下面的第1点。

例如,要确保视图可以接收UIDrop交互,或添加UIDrag交互,或实际上可能受其他软件影响的任何其他交互(例如从其他软件拖到我的iPad上)。 / p>

因此,我创建了一些有趣的代码,它们实际上只是包装了视图“ Content”,并在实际实例化视图时被调用。我想放弃Controller部分,但在方程式的iOS部分似乎不可能,只有macOS。嘘无论如何,我知道这些功能最终可能会添加到SwiftUI中,因此该桥的明显目标是尽可能透明,因此只要有适当的方法添加到SwiftUI中,我就可以将其删除。

不管您信不信,它通常都能正常工作!但是这里有一个顽固的问题和一个错误,也许有人实际上做了类似的事情,并且实际上可以帮助我解决问题。也许我的代码可以作为一个很好的起点来帮助其他人。

(SwiftUI 11.0 beta 5)

  1. (错误和主要问题)似乎帧大小没有传播。因此,我班的父类实际上必须对.frame(width: something, height: something)进行硬编码,否则我的视图的大小将不合适。这是有道理的,因为我实际上没有传播这些值也不从Content检索它们。我尝试拥抱尺寸,但未发送相框尺寸。我应该在哪里以及如何全面获得该价值。我的代码的目标是使这座桥对SwiftUI,开发人员和最终用户尽可能地精益,高效和不可见。

  2. (Nitpick)我发现实际上没有调用viewWillAppearviewDidLoad和其他加载时间操作。例如,在观看时不会调用viewWillAppear。如果我更深入地导航并返回,则将调用viewWillAppear,但不是第一次显示。因此,我诉诸于创建一个临时的“初始化”变量。我在做什么错(如果有的话)?

  3. 也许我也忘记了在类和结构之间传输其他数据。例如,UIViewController实际上使对象的背景为白色。它可能是透明的,但对我而言并不重要(我想您可以添加self.view.backgroundColor = .clear)。如果您想到改进,我将非常高兴知道它!

import SwiftUI

/// Creates a SwiftUI to UIKit to SwiftUI bridge, where a lambda is called on viewWillAppear
struct UIKitBridgeView<Content>: View where Content : View {
    var content: () -> Content
    var onViewWillAppear: (UIView)->Void

    init(onViewWillAppear: @escaping (UIView)->Void, _ content: @escaping () -> Content) {
        self.content = content
        self.onViewWillAppear = onViewWillAppear
    }

    class Coordinator: NSObject {

    }

    /// This is the class in UIKit that can host a SwiftUI "Content" type.
    class HostingController: UIHostingController<Content> {
        var onViewWillAppear: ((UIView)->Void)?
        var initialized: Bool = false

        init(_ content: () -> Content) {
            super.init(rootView: content())
            self.onViewWillAppear = nil
        }

        init(_ content: () -> Content, _ onViewWillAppear: @escaping (UIView)->Void) {
            super.init(rootView: content())
            self.onViewWillAppear = onViewWillAppear
        }

        @objc required dynamic init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            self.onViewWillAppear = nil
        }

        func update(context: UIViewControllerRepresentableContext<ViewControllerRepresentable>) {
            if !initialized {
                initialized = true
                if let onViewWillAppear = self.onViewWillAppear {
                    onViewWillAppear(self.view)
                }
                // self.view.setContentHuggingPriority(.defaultHigh, for: .vertical) doesn't seem to do anything...
                // self.view.setContentHuggingPriority(.defaultHigh, for: .horizontal)
            }
        }
    }

    /// This is the struct in SwiftUI that can host a UIKit's ViewController
    struct ViewControllerRepresentable : UIViewControllerRepresentable {
        let content: () -> Content
        var onViewWillAppear: ((UIView)->Void)?

        init(_ content: @escaping () -> Content) {
            self.content = content
            self.onViewWillAppear = nil
        }

        init(_ content: @escaping () -> Content, _ onViewWillAppear: @escaping (UIView)->Void) {
            self.content = content
            self.onViewWillAppear = onViewWillAppear
        }

        func makeUIViewController(context: Context) -> HostingController {
            self.onViewWillAppear != nil ?
                HostingController(self.content, self.onViewWillAppear!) :
                HostingController(self.content)
        }

        func updateUIViewController(_ hostingController: HostingController, context: Context) {
            hostingController.update(context: context)
        }

        func makeCoordinator() -> Coordinator {
            Coordinator()
        }
    }

    var body: some View {
        ViewControllerRepresentable(self.content, self.onViewWillAppear)
    }
}

对于第1点不完整的示例:

UIKitBridgeView(onViewWillAppear: { (view) in
    if let delegate = self.appState.dragInteractionDelegate {
        view.addInteraction(self.createDraggable(delegate))
    }
}) {
    Spacer()            // Insert what you want here
}.frame(width: 50, height: 50)

.frame(width, height)真的让我很烦!

0 个答案:

没有答案