【问题标题】:Initialising a SwiftUI View via a Swift generic通过 Swift 泛型初始化 SwiftUI 视图
【发布时间】:2021-04-29 09:11:17
【问题描述】:

我正在将 Pull To Refresh 添加到基于 SwiftUI 的应用程序中。为此,我不得不退回到 UIKit。由于我想跨多个表和数据模型(使用共享协议)使用 Pull To Refresh,因此我想使用泛型。

我传入了我的dataModel的类型,我还想传入我想在scrollView中实例化的SwiftUI View的类型。

但是,当我添加我声明支持协议“视图”的通用类型 V 时,我在代码尝试实例化视图时遇到编译错误。

类型“V”没有成员“init”

如果我放弃通用类型“V”,而是硬编码一个特定的 SwiftUI 视图类型来实例化,比如 DataView,这一切都很好 - 但这并没有给我所需的视图类型的灵活性。

请问我该如何解决这个编译错误?

import SwiftUI

struct GenericRefreshView<T, V>: UIViewRepresentable where T:ObservableObject, T:dataModel, V: View {

    @EnvironmentObject var dataModel: T

    func makeCoordinator() -> Coordinator {
        Coordinator(self, regs: dataModel)
    }

    let size: CGSize

    func makeUIView(context: Context) -> UIScrollView {
        let scrollView = UIScrollView()
        scrollView.refreshControl = UIRefreshControl()
        scrollView.refreshControl?.addTarget(context.coordinator, action: #selector(Coordinator.handleRefreshControl(sender:)), for: .valueChanged)

        // *******************************************************************************
        let refreshVC = UIHostingController(rootView: V()) // Type 'V' has no member 'init'
        // *******************************************************************************

        refreshVC.view.frame = CGRect(x: 0, y: 0, width: size.width, height: size.height)

        scrollView.addSubview(refreshVC.view)

        return scrollView
    }

    func updateUIView(_ uiView: UIScrollView, context: Context) {}

    class Coordinator: NSObject {
        var refreshScrollView: GenericRefreshView
        var dataModel: T

        init(_ refreshScrollView: GenericRefreshView, dataM: T) {
            self.refreshScrollView = refreshScrollView
            self.dataModel = dataM
        }

        @objc func handleRefreshControl (sender: UIRefreshControl) {
            self.dataModel.refresh()
            sender.endRefreshing()
        }
    }
}

【问题讨论】:

    标签: swift generics swiftui


    【解决方案1】:

    你可以要求它在外部注入,比如

    struct GenericRefreshView<T, V>: UIViewRepresentable where T:ObservableObject, T:dataModel, V: View {
    
        @EnvironmentObject var dataModel: T
        let contentView: V                      // here !!
        // let builder: () -> V                 // alternate, as view builder
    
        func makeCoordinator() -> Coordinator {
            Coordinator(self, regs: dataModel)
        }
    
        let size: CGSize
    
        func makeUIView(context: Context) -> UIScrollView {
            let scrollView = UIScrollView()
            scrollView.refreshControl = UIRefreshControl()
            scrollView.refreshControl?.addTarget(context.coordinator, action: #selector(Coordinator.handleRefreshControl(sender:)), for: .valueChanged)
    
            let refreshVC = UIHostingController(rootView: self.contentView)    // here !!
    
       ...
    

    更新:简化以避免遗漏部分,这里是使用演示

    struct DemoView: View {
        var body: some View {
            GenericRefreshView(dataModel: TestViewModel(), 
                             contentView: Text("Demo"), 
                                    size: CGSize(width: 100, height: 100))
        }
    }
    
    struct GenericRefreshView<T, V>: UIViewRepresentable where T:ObservableObject, V: View {
    
        @ObservedObject var dataModel: T     // << EnvironmentObject is internal, so cannot be inferred from input arguments, so not usable here
        let contentView: V
        let size: CGSize
    

    【讨论】:

    • 感谢您的回复。对不起,我不关注。您是说我应该将已经实例化的视图作为参数传递吗?在那种情况下,我不会失去通用方面吗?你能告诉我怎么称呼它吗?谢谢。
    • 感谢您的更新,我现在已经按照您的建议工作了 - 非常感谢。我希望必须指定 GenericRefreshView 类型,但显然 Swift 算出来了!干杯。
    猜你喜欢
    • 1970-01-01
    • 2016-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多