在iOS App中构建可重用的通用UITableViewController
TableView Controller是一个必不可少的UIKit组件,几乎每个iOS应用程序都使用TableView Controller组件来显示列表中的数据。 当我们要在UITableViewController
显示不同类型的数据时,大多数时候我们会创建一个新的子类来显示相关的数据类型。 这种方法有效,但是如果我们的应用程序中有许多不同类型的数据,则可能导致重复和维护困难。
我们如何解决这个问题? 一种方法是,我们可以使用Swift通用抽象数据类型使用简单抽象来创建通用UITableViewController
子类,该子类可用于使用Swift通用约束配置和显示不同种类的数据。
您可以在GitHub存储库中找到并构建源项目:
alfianlosari / GenericTableViewController
通用UITableViewController实现的示例– alfianlosari / GenericTableViewController
github.com
我们创建一个名为GenericTableViewController
的UITableViewController
子类,并添加2种类型的Generic T
和Cell
。 我们添加了约束,即Cell
必须是UITableViewCell
子类。 T
将用作数据的抽象,而Cell
将被注册到UITableView
并出队以将每行的数据显示为UITableViewCell
。
类 GenericTableViewController :UITableViewController { 变量 :[T]
var configure:(Cell,T)-> Void
var selectHandler:(T)-> Void init (items:[T],配置: @转义 (Cell,T)-> Void,selectHandler: @转义 (T)-> Void){
自我 .items =项目
自我 .configure =配置
自我 .selectHandler = selectHandler
超级 .init(样式:.plain)
self .tableView.register(Cell。self,forCellReuseIdentifier:“ Cell”)
} ...
}
让我们看一下初始化程序,它接受3个参数:
-
T
泛型的数组:这将被分配为驱动UITableViewDataSource
的实例变量。 - 配置闭包:当表视图使要在每行中显示的单元格出队时,将通过
T
数据和Cell
来调用此配置闭包。 在这里,我们设置如何使用数据显示UITableViewCell
。 (通过在参数中明确声明Cell
的类型,编译器将能够隐式推断Cell
的类型,只要它是UITableViewCell
的子类即可) - 选定的处理程序关闭。 当用户选择/点击单元格中的行时,将调用闭包并传递所选内容。 在这里,我们可以添加当用户点击一行时将被调用的逻辑或动作。
初始化程序将3个参数中的每一个分配为该类的实例变量,然后使用可重用的标识符将Cell
注册到UITableView
,该标识符可用于使UITableViewCell
为数据源出队。
类 GenericTableViewController :UITableViewController {.... // 1
覆盖 func tableView( _ tableView:UITableView,numberOfRowsInSection部分:Int)-> Int {
返回 items.count
} // 2
覆盖 func tableView( _ tableView:UITableView,cellForRowAt indexPath:IndexPath)-> UITableViewCell {
让 cell = tableView.dequeueReusableCell(withIdentifier:“ Cell”,for:indexPath) 为 ! 细胞
让 item = items [indexPath.row]
配置(单元格,项)
返回单元
} // 3
覆盖 func tableView( _ tableView:UITableView,didSelectRowAt indexPath:IndexPath){
tableView.deselectRow(at:indexPath,animation: true )
让 item = items [indexPath.row]
selectHandler(项目)
}
}
这是我们需要覆盖的UITableViewDataSource
和UITableViewDelegate
方法:
-
tableView:numberOfRowsInSection:
这里我们只是返回T
对象数组中的数据数 -
tableView:cellForRowAtIndexPath:
我们使用可重用的标识符使UITableViewCell
出队,然后将其强制转换为Cell
。 然后,我们使用索引路径行从T
数组中获取数据。 之后,我们调用配置闭包,以传递该单元格以及要在显示之前对其进行自定义的数据。 -
tableView:didSelectRowAtIndexPath:
这里,我们只是使用索引路径行从数组中获取数据,然后调用选定的处理程序闭包并传递数据。
要尝试使用不同类型的对象尝试GenericTableViewController
,我们创建两个简单的结构Person
和Film
。 在每个结构内,我们创建一个静态计算变量,该变量将为每个结构返回一个硬编码的存根对象数组。
struct Person {
命名:字符串
静态 var stubPerson:[人员] {
返回 [
人(姓名:“马克·哈米尔”),
人(姓名:“哈里森·福特”),
人(姓名:“嘉莉·费舍尔”),
人(姓名:“ Hayden Christensen”),
人(姓名:“ Ewan McGregor”),
人(姓名:“ Natalie Portman”),
人(姓名:“利亚姆·尼森”)
]
}
} struct Film { 让标题:String
让 releaseYear:Int 静态 var stubFilms:[Film] {
返回 [
电影(标题:“星球大战:新希望”,发行年份:1978),
电影(标题:“星球大战:帝国反击”,发行年份:1982),
电影(标题:“星球大战:绝地归来”,发行年份:1984),
电影(标题:“星球大战:幻影威胁”,发行年份:1999),
电影(标题:“星球大战:克隆人战争”,发行年份:2003),
电影(标题:“星球大战:西斯的复仇”,发行时间:2005年)]
}
}
设置Person GenericTableViewController
让 personVC = GenericTableViewController(items:Person.stubPerson,配置:{(单元格:UITableViewCell,person) 在
cell.textLabel?.text = person.name
}){(person) in
打印(person.name)
}
我们将使用标准的UITableViewCell
Basic样式显示“ Person
”列表。 在这里,我们实例化GenericTableViewController
传递了Person
对象的数组。 完成闭包将标准UITableViewCell
用作Cell
的类型,在配置内部,我们仅使用人员名称分配textLabel
文本属性。 对于所选的处理程序关闭,我们只需将所选人员的姓名打印到控制台即可。 您可以在这里看到Swift隐式类型引用的强大功能,编译器将自动用Person
结构替换T
泛型。
设置电影GenericTableViewController
类 SubtitleTableViewCell:UITableViewCell {
覆盖 init (样式:UITableViewCell.CellStyle,reuseIdentifier:字符串?){
超级 .init(样式:.subtitle,复用标识符: nil )
}
...
}
对于Film
,我们将使用带有字幕样式的UITableViewCell
来显示它。 为了做到这一点,我们需要创建覆盖默认样式的子类以使用我们称为SubtitleTableViewCell
的Subtitle样式。
让 filmsVC = GenericTableViewController(items:Film.stubFilms,配置:{(单元格:SubtitleTableViewCell,电影) 在
cell.textLabel?.text = film.title
cell.detailTextLabel?.text =“ \(film.releaseYear)”
}){(电影) 在
打印(film.title)
}
我们实例化了传递Film
对象数组的GenericTableViewController
。 对于配置闭包,我们将Cell
参数的单元格类型显式设置为SubtitleTableViewCell
,然后在闭包内部,我们仅使用影片的标题和发行年份设置单元格textLabel
和detailTextLabel
文本属性。 对于所选的处理程序闭包,我们只需将所选电影的标题打印到控制台
使用UITabBarController作为容器视图控制器的最终集成
@UIApplicationMain 类 AppDelegate:UIResponder,UIApplicationDelegate {变量窗口:UIWindow? func application( _ application:UIApplication,didFinishLaunchingWithOptions launchOptions:[UIApplication.LaunchOptionsKey: Any ]?)-> Bool {//实例化人物和电影表视图控制器
... 让 tabVC = UITabBarController(nibName: nil ,bundle: nil )
tabVC.setViewControllers([
UINavigationController(rootViewController:personVC),
UINavigationController(rootViewController:filmsVC)
],动画: false )
窗口= UIWindow(框架:UIScreen.main.bounds)
窗口?.rootViewController = tabVC
窗口?.makeKeyAndVisible()
返回 真
}
}
为了在iOS项目中显示它,我们将使用一个UITabBarController
,其中包含GenericTableViewController
的Person
和Film
实例作为ViewController。 我们将标签栏控制器设置为UIWindow
根视图控制器,并将每个通用表视图控制器嵌入UINavigationController
我们终于设法使用Swift泛型为UITableViewController
创建了一个abstract
容器类。 这种方法确实可以帮助我们将相同的UITableViewController
重用为不同类型的数据源,但仍然可以使用符合UITableViewCell
的通用Cell
进行自定义。 Swift泛型是一个非常了不起的范例,我们可以用来创建非常强大的抽象。 雨后春笋般快乐,Crusty long万岁。