iOS - 使用泛型重用相同的 TableViewController
iOS - Reuse same TableViewController with Generics
我关注了一些很棒的文章以实现可与泛型一起重用的 BaseTableViewController。
它正在运行,这是一个简单的例子:
if let mountains = viewModel?.model["mountain"] as? ResponseMountain {
let mountainTableViewController = BaseTableViewController(items: mountains.result, config: { (cell, item) in
cell.textLabel?.text = item.name
}, style: .plain)
mountainTableViewController.title = "Mountains"
self.navigationController?.pushViewController(mountainTableViewController, animated: true)
}
想象一下,我想显示我所在国家/地区的山脉的简单列表,而当您 select 山脉时,您会显示该山脉内所有可用地块的列表。
这就是为什么我想重用相同的 TableViewController,但使用不同的数据。
我不知道如何用我的泛型实现来处理它。
这是我当前的实现:
final class BaseTableViewController<Item>: UITableViewController {
var items: [Item] {
didSet {
tableView.reloadData()
}
}
let config: (UITableViewCell, Item) -> ()
init(items: [Item], config: @escaping (UITableViewCell, Item) -> (), style: UITableViewStyle) {
self.items = items
self.config = config
super.init(style: style)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "cellIdentifier")
cell.textLabel?.textColor = UIColor.white
let item = items[indexPath.row]
config(cell, item)
return cell
}
}
它高效、简单,适用于不同的用例。
但是我如何使用泛型实现自定义 didSelectRowAt
,以便使用我刚刚 select 编辑的行中的数据推送同一 BaseViewController 的实例?
我会按照你的方式去做 cellForRowAtIndexPath
。将类型为 (Item) -> ()
的另一个闭包传递给 init
。然后在 didSelectRowAt
中调用该闭包并传递所选项目。
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = items[indexPath.row]
didSelect(item)
}
我可能会采取这样的方法:
首先,为我们的 table 数据项定义协议和一个实现 object 以配合它:
protocol TableItem {
var title: String { get set }
var items: [TableItem]? { get set }
}
struct TableItemImp: TableItem {
var title: String
var items: [TableItem]?
}
接下来定义 UITableViewCell 的扩展来处理单元格配置:
extension UITableViewCell {
func configure<T>(with item: T) {
if let item = item as? TableItem {
// configure based on TableItem type
textLabel?.text = item.title
}
}
}
这是您的基础 table 视图 class 的更新实现,用于使用此设置并提供另一个基础 table 视图控制器,其中包含在子列表中定义的列表:
final class BaseTableViewController<Item>: UITableViewController {
let cellId = "cellIdentifier"
var items: [Item] = [] {
didSet {
tableView.reloadData()
}
}
init(items: [Item], style: UITableViewStyle) {
super.init(style: style)
configure(with: items)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
internal func configure(with items: [Item] = []) {
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
self.items = items
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let item = items[indexPath.row]
cell.configure(with: item)
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let item = items[indexPath.row]
guard let vc = getDetailController(item: item) else {
return
}
present(vc, animated: true, completion: nil)
}
internal func getDetailController(item: Item) -> UITableViewController? {
guard let item = item as? TableItem, let items = item.items else {
return nil
}
return BaseTableViewController<TableItem>(items: items, style: .plain)
}
}
我使用此设置代码对其进行了测试。
func setupTableView() {
let items = [
TableItemImp(title: "Alps", items: [
TableItemImp(title: "One", items: nil)
]),
TableItemImp(title: "Appalachian", items: [
TableItemImp(title: "Two", items: nil)
]),
TableItemImp(title: "Rockies", items: [
TableItemImp(title: "Three", items: nil)
])
]
let tableVC = BaseTableViewController<TableItem>.init(items: items, style: .plain)
present(tableVC, animated: true, completion: nil)
}
我在 viewDidAppear()
的测试视图控制器中调用此代码,它向您的基础 table 视图控制器提供顶级列表。如果您单击 "Rockies"
,那么它将显示另一个列表控制器,显示一个标题为 "Three"
的单元格
我关注了一些很棒的文章以实现可与泛型一起重用的 BaseTableViewController。
它正在运行,这是一个简单的例子:
if let mountains = viewModel?.model["mountain"] as? ResponseMountain {
let mountainTableViewController = BaseTableViewController(items: mountains.result, config: { (cell, item) in
cell.textLabel?.text = item.name
}, style: .plain)
mountainTableViewController.title = "Mountains"
self.navigationController?.pushViewController(mountainTableViewController, animated: true)
}
想象一下,我想显示我所在国家/地区的山脉的简单列表,而当您 select 山脉时,您会显示该山脉内所有可用地块的列表。
这就是为什么我想重用相同的 TableViewController,但使用不同的数据。
我不知道如何用我的泛型实现来处理它。
这是我当前的实现:
final class BaseTableViewController<Item>: UITableViewController {
var items: [Item] {
didSet {
tableView.reloadData()
}
}
let config: (UITableViewCell, Item) -> ()
init(items: [Item], config: @escaping (UITableViewCell, Item) -> (), style: UITableViewStyle) {
self.items = items
self.config = config
super.init(style: style)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: "cellIdentifier")
cell.textLabel?.textColor = UIColor.white
let item = items[indexPath.row]
config(cell, item)
return cell
}
}
它高效、简单,适用于不同的用例。
但是我如何使用泛型实现自定义 didSelectRowAt
,以便使用我刚刚 select 编辑的行中的数据推送同一 BaseViewController 的实例?
我会按照你的方式去做 cellForRowAtIndexPath
。将类型为 (Item) -> ()
的另一个闭包传递给 init
。然后在 didSelectRowAt
中调用该闭包并传递所选项目。
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let item = items[indexPath.row]
didSelect(item)
}
我可能会采取这样的方法:
首先,为我们的 table 数据项定义协议和一个实现 object 以配合它:
protocol TableItem {
var title: String { get set }
var items: [TableItem]? { get set }
}
struct TableItemImp: TableItem {
var title: String
var items: [TableItem]?
}
接下来定义 UITableViewCell 的扩展来处理单元格配置:
extension UITableViewCell {
func configure<T>(with item: T) {
if let item = item as? TableItem {
// configure based on TableItem type
textLabel?.text = item.title
}
}
}
这是您的基础 table 视图 class 的更新实现,用于使用此设置并提供另一个基础 table 视图控制器,其中包含在子列表中定义的列表:
final class BaseTableViewController<Item>: UITableViewController {
let cellId = "cellIdentifier"
var items: [Item] = [] {
didSet {
tableView.reloadData()
}
}
init(items: [Item], style: UITableViewStyle) {
super.init(style: style)
configure(with: items)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
internal func configure(with items: [Item] = []) {
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellId)
self.items = items
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
let item = items[indexPath.row]
cell.configure(with: item)
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let item = items[indexPath.row]
guard let vc = getDetailController(item: item) else {
return
}
present(vc, animated: true, completion: nil)
}
internal func getDetailController(item: Item) -> UITableViewController? {
guard let item = item as? TableItem, let items = item.items else {
return nil
}
return BaseTableViewController<TableItem>(items: items, style: .plain)
}
}
我使用此设置代码对其进行了测试。
func setupTableView() {
let items = [
TableItemImp(title: "Alps", items: [
TableItemImp(title: "One", items: nil)
]),
TableItemImp(title: "Appalachian", items: [
TableItemImp(title: "Two", items: nil)
]),
TableItemImp(title: "Rockies", items: [
TableItemImp(title: "Three", items: nil)
])
]
let tableVC = BaseTableViewController<TableItem>.init(items: items, style: .plain)
present(tableVC, animated: true, completion: nil)
}
我在 viewDidAppear()
的测试视图控制器中调用此代码,它向您的基础 table 视图控制器提供顶级列表。如果您单击 "Rockies"
,那么它将显示另一个列表控制器,显示一个标题为 "Three"