简体   繁体   English

在 SwiftUI 中实例化自定义视图时,如何动态声明变量类型然后将其用作参数?

[英]How can I dynamically declare a variable type then use it as an argument when instantiating a custom view in SwiftUI?

What I'm doing:我在做什么:

I'm making a reusable tableView and have just about finished, but I'm unable to reference properties in my view model because I have declared my model type as AnyObject inside the table view struct.我正在制作一个可重用的tableView并且刚刚完成,但是我无法在我的视图 model 中引用属性,因为我已将我的 model 类型声明为表视图结构中的AnyObject

I want to dynamically declare the variable type when instantiating the tableView .我想在实例化tableView时动态声明变量类型。

For example, this is how I use my custom table view:例如,这是我使用自定义表格视图的方式:

struct MyView: View {
    @EnvironmentObject var myViewModel: MyViewModel

    var body: some View {

        return

          CustomTableView(model: myViewModel as MyViewModel)
    }
}

As you can see, I have to declare the type in my view because I'm using the AnyObject type in my table view struct.如您所见,我必须在视图中声明类型,因为我在表视图结构中使用AnyObject类型。 This is because the model will vary depending on where I'm using the custom table view, and so I need the flexibility.这是因为 model 会根据我使用自定义表格视图的位置而有所不同,因此我需要灵活性。

This is my table view Struct:这是我的表视图结构:

struct CustomTableView: UIViewRepresentable {

    var model: AnyObject
    class Coordinator: NSObject, UITableViewDelegate, UITableViewDataSource {

        var customTableView: CustomTableView
        let cellIdentifier = "MyCell"

        init(_ customTableView: customTableView) {
            self.customTableView = customTableView
        }

        func numberOfSections(in tableView: UITableView) -> Int {
            return 1
        }

        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 7
        }

        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

            let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! MyTableViewCell
            return cell
        }
    }

    func makeCoordinator() -> DabbleTableView.Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> UITableView {  
        let cellIdentifier = "MyCell"
        let tableView = UITableView()
        tableView.delegate = context.coordinator
        tableView.dataSource = context.coordinator
        tableView.register(MyTableViewCell.self, forCellReuseIdentifier: cellIdentifier)
        tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
        return tableView    
    }

    func updateUIView(_ uiViewController: UITableView, context: Context) {           
    }
}

What I want to do:我想做的事:

Either have the table view automatically use the ViewModel type name declared when instantiating the table view, or pass in the correct ViewModel type when instantiating the table view.要么让表视图自动使用在实例化表视图时声明的ViewModel类型名称,要么在实例化表视图时传入正确的ViewModel类型。

It's not clear to me how this could be done.我不清楚如何做到这一点。 I was thinking type alias might work, but the more I read into it, the more I realised it wasn't the right solution.我在想类型别名可能有用,但我读得越多,我就越意识到这不是正确的解决方案。

Passing in the type as a string doesn't work because Swift will recognise the argument as a string.将类型作为字符串传递不起作用,因为 Swift 会将参数识别为字符串。

What would be the cleanest way to do this?最干净的方法是什么?

Thanks in advance.提前致谢。

The possible approach is to use generics (conforming also to any custom protocol if/when needed), so you don't need to cast types and use it as可能的方法是使用 generics (如果/需要时也符合任何自定义协议),因此您不需要转换类型并将其用作

struct MyView: View {
    @EnvironmentObject var myViewModel: MyViewModel

    var body: some View {
        CustomTableView(model: myViewModel)
    }
}

so CustomTableView should be declared as所以CustomTableView应该声明为

struct CustomTableView<Model:ObservableObject>: UIViewRepresentable {

    var model: Model

    ...

Tested with Xcode 11.4 / iOS 13.4使用 Xcode 11.4 / iOS 13.4 测试

Update: below is compilable example更新:下面是可编译的例子

// base protocol for CustomTableView model
protocol CustomTableViewModel {
    func numberOfSections() -> Int
    func numberOfRows(in section: Int) -> Int
}

// some specific model
class MyViewModel: ObservableObject, CustomTableViewModel {
    func numberOfSections() -> Int { 1 }
    func numberOfRows(in section: Int) -> Int { 7 }
}

// usage
struct MyView: View {
    @EnvironmentObject var myViewModel: MyViewModel

    var body: some View {
        CustomTableView(model: myViewModel)
    }
}

// generic table view
struct CustomTableView<Model:ObservableObject & CustomTableViewModel>: UIViewRepresentable {

    var model: Model

    class Coordinator: NSObject, UITableViewDelegate, UITableViewDataSource {

        var customTableView: CustomTableView
        let cellIdentifier = "MyCell"

        init(_ customTableView: CustomTableView) {
            self.customTableView = customTableView
        }

        func numberOfSections(in tableView: UITableView) -> Int {
            customTableView.model.numberOfSections()
        }

        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            customTableView.model.numberOfRows(in: section)
        }

        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

            let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! MyTableViewCell
            return cell
        }
    }

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

    func makeUIView(context: Context) -> UITableView {
        let cellIdentifier = "MyCell"
        let tableView = UITableView()
        tableView.delegate = context.coordinator
        tableView.dataSource = context.coordinator
        tableView.register(MyTableViewCell.self, forCellReuseIdentifier: cellIdentifier)
        tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
        return tableView
    }

    func updateUIView(_ uiViewController: UITableView, context: Context) {
    }
}

class MyTableViewCell: UITableViewCell { }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM