事实上,任何UIKit或AppKit视图都可以被包装以兼容wift,这是非常有用的,因为这在某种程度上提供了一个“逃生舱口”,每当SwiftUI还不支持特定类型的控件或UI元素时。

然而,如果我们需要依靠大量的(没有更好的词)“历史遗留”视图,必须不断地为他们其中的每个都写包装可能会开始变得有点乏味,尤其是对于简单的观点,不需要任何复杂的逻辑,或视图,我们只是想在一个地方使用。

例如,假设我们想要使用UIActivityIndicatorView在一个基于SwiftUI的iOS应用程序中显示一个加载旋转器。为了做到这一点,我们必须编写一个包装器,看起来像这样:

struct ActivityIndicator: UIViewRepresentable {
    func makeUIView(context: Context) -> UIActivityIndicatorView {
        UIActivityIndicatorView(style: .medium)
    }

    func updateUIView(_ view: UIActivityIndicatorView, context: Context) {
        view.startAnimating()
    }
}

当写一个包装,符合UIViewRepresentation(或在Mac上的NSViewRepresentation)不是一个巨大的任务-它不是很好,如果我们可以只是包装任何遗留视图内联,就在我们需要使用它?

让我们通过写一个泛型类型来实现能被用来包装任何UIView。让我们称它为Wrap,并让它有两个闭包,每个对应一个UIViewRepresentation要求的方法-像这样:

struct Wrap<Wrapped: UIView>: UIViewRepresentable {
    typealias Updater = (Wrapped, Context) -> Void

    var makeView: () -> Wrapped
    var update: (Wrapped, Context) -> Void

    init(_ makeView: @escaping @autoclosure () -> Wrapped,
         updater update: @escaping Updater) {
        self.makeView = makeView
        self.update = update
    }

    func makeUIView(context: Context) -> Wrapped {
        makeView()
    }

    func updateUIView(_ view: Wrapped, context: Context) {
        update(view, context)
    }
}

请注意上面@autoclosure的用法,它将使我们能够继续遵循UIViewRepresentation的约定并惰性地创建我们的视图,而不需要在调用站点上使用任何额外的语法。

然而,当更新我们的视图时,新的Wrap类型当前要求我们始终同时处理视图本身和当前上下文。 虽然获得上下文参数可能对一些场景比较重要,让我们让它可选,通过引入了两个方便的api,会让我们要么接受我们的view作为唯一参数,或者选择完全舍弃更新,当我们的视图是完全静态时;

extension Wrap {
    init(_ makeView: @escaping @autoclosure () -> Wrapped,
         updater update: @escaping (Wrapped) -> Void) {
        self.makeView = makeView
        self.update = { view, _ in update(view) }
    }

    init(_ makeView: @escaping @autoclosure () -> Wrapped) {
        self.makeView = makeView
        self.update = { _, _ in }
    }
}

有了以上这些,我们现在可以轻松地将任何UIView完全内联,同时也可以在底层状态发生变化时更新它——像这样:

struct ContentView: View {
    @ObservedObject var viewModel: ViewModel

    var body: some View {
        ZStack {
            ...
            Wrap(UIActivityIndicatorView()) {
                if self.viewModel.isLoading {
                    $0.startAnimating()
                } else {
                    $0.stopAnimating()
                }
            }
        }
    }
}

非常好!当然,这并不意味着我们应该完全放弃为某些视图构建适当的包装器,但对于更简单的视图,上面的包装类型非常方便

原文链接