在 SwiftUI 中实例化自定义视图时,如何动态声明变量类型然后将其用作参数?
Posted
技术标签:
【中文标题】在 SwiftUI 中实例化自定义视图时,如何动态声明变量类型然后将其用作参数?【英文标题】:How can I dynamically declare a variable type then use it as an argument when instantiating a custom view in SwiftUI? 【发布时间】:2020-04-08 23:40:21 【问题描述】:我在做什么:
我正在制作一个可重复使用的 tableView
并且即将完成,但我无法在我的视图模型中引用属性,因为我已在表视图结构中将我的模型类型声明为 AnyObject
。
我想在实例化tableView
时动态声明变量类型。
例如,这是我使用自定义表格视图的方式:
struct MyView: View
@EnvironmentObject var myViewModel: MyViewModel
var body: some View
return
CustomTableView(model: myViewModel as MyViewModel)
如您所见,我必须在视图中声明类型,因为我在表视图结构中使用AnyObject
类型。这是因为模型会根据我使用自定义表格视图的位置而有所不同,因此我需要灵活性。
这是我的表格视图结构:
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)
我想做的事:
要么让表格视图自动使用在实例化表格视图时声明的ViewModel
类型名称,要么在实例化表格视图时传入正确的ViewModel
类型。
我不清楚如何做到这一点。我在想类型别名可能会起作用,但我越读越觉得这不是正确的解决方案。
将类型作为字符串传递不起作用,因为 Swift 会将参数识别为字符串。
最干净的方法是什么?
提前致谢。
【问题讨论】:
【参考方案1】:可能的方法是使用泛型(如果/需要时也符合任何自定义协议),因此您不需要转换类型并将其用作
struct MyView: View
@EnvironmentObject var myViewModel: MyViewModel
var body: some View
CustomTableView(model: myViewModel)
所以CustomTableView
应该声明为
struct CustomTableView<Model:ObservableObject>: UIViewRepresentable
var model: Model
...
使用 Xcode 11.4 / ios 13.4 测试
更新:以下是可编译的示例
// 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
【讨论】:
嗨,这部分有效。但我仍然无法在我的视图模型中引用属性和函数。例如,在 numberOfRowsInSection 委托方法中,我做了这个 self.customTableView.model.nameOfFunction() 并且我仍然无法访问该函数。我有大约 5 个不同的 ViewModel 将用于这个表视图。表视图中声明的模型名称不能是静态的。它需要能够与我在表格视图中使用的任何视图模型一起工作。我应该能够访问使用的模型中的属性和函数,并且不需要手动编辑 tableview 文件 @LondonGuy,是的,我展示了共同的方向。您需要为预期模型创建自定义协议,并将其添加到泛型中。 您能给我一个基本示例,说明您的视图模型在自定义协议下的外观吗?之后我可以做进一步的研究。 @LondonGuy,请参阅更新部分以上是关于在 SwiftUI 中实例化自定义视图时,如何动态声明变量类型然后将其用作参数?的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI当总内容宽度小于屏幕宽度时,如何在动态水平滚动视图中居中内容