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.
I want to dynamically declare the variable type when instantiating the 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. This is because the model will vary depending on where I'm using the custom table view, and so I need the flexibility.
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.
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.
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
struct MyView: View {
@EnvironmentObject var myViewModel: MyViewModel
var body: some View {
CustomTableView(model: myViewModel)
}
}
so CustomTableView
should be declared as
struct CustomTableView<Model:ObservableObject>: UIViewRepresentable {
var model: Model
...
Tested with 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 { }
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.