UITableView功能強大,可是使用delegate設計模式的DataSource真的很不舒服。好比說:javascript
冗長的函數簽名是這樣的:java
func numberOfSections(in: UITableView) -> Int
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 複製代碼
是否能夠給它一個DataSource對象,它本身就能夠顯示內容便可呢,就像這樣:swift
tableview.Datasource = [["java","swift","js"],["java","swift","js"]]設計模式
它應該能夠數組
這是可能的,實際上,以下類Table封裝完畢,使用的時候,就是能夠達成但願的效果的,使用Table,你沒必要在本身編寫這些函數,代碼以下:app
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
let page = Page()
page.view.backgroundColor = .blue
self.window!.rootViewController = page
self.window?.makeKeyAndVisible()
return true
}
}
class Page: UIViewController {
var a : Table!
override func viewDidLoad() {
super.viewDidLoad()
a = Table()
a.ds = [["java","swift","js"],["java","swift","js"]]
a.frame = CGRect(x: 0,y: 50,width: 300,height: 500)
self.view.addSubview(a)
}
}
class Table : UITableView,UITableViewDataSource,UITableViewDelegate{
public var ds : [[Any]]
override init(frame: CGRect, style: UITableViewStyle) {
ds = []
super.init(frame:frame,style:style)
self.dataSource = self
self.delegate = self
}
required init?(coder aDecoder: NSCoder) {
ds = []
super.init(coder:aDecoder)
}
func numberOfSections(in: UITableView) -> Int {
return ds.count
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ds[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let arr = ds
let a = UITableViewCell(style: .default, reuseIdentifier: nil)
a.textLabel?.text = String(describing:arr[indexPath.section][indexPath.row])
return a
}
}複製代碼
這個案例有不少限制,好比數據源內的數據項,只能是String類型。若是想要通常的對象做爲數據項,更加花哨的Cell做爲外觀,有辦法嗎?關鍵就是,讓Cell的類型是參數化的,能夠傳遞和修改的。爲此,咱們須要在Table內加入一個cellClass的類,它能夠由使用這套封裝的開發者傳遞進來。以下案例,已經完成了此工做,並提供了對象化的數據項做爲案例:框架
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
let page = Page()
page.view.backgroundColor = .blue
self.window!.rootViewController = page
self.window?.makeKeyAndVisible()
return true
}
}
class Page: UIViewController {
var a : Table!
override func viewDidLoad() {
super.viewDidLoad()
var s = DoubleString("Paris","Charles de Gaulle")
var t = DoubleString("Rome","FCO")
a = Table()
a.cellClass = DoubleStringCell.self
a.ds = [[s,t]]
a.load()
a.frame = CGRect(x: 0,y: 50,width: 300,height: 500)
self.view.addSubview(a)
}
}
class DoubleString{
var s1 : String
var s2 : String
init(_ s1:String,_ s2:String){
self.s1=s1
self.s2=s2
}
}
class DoubleStringCell:Cell{
var l1 : UILabel?=UILabel()
var l2 : UILabel?=UILabel()
override func layoutSubviews() {
l1?.frame = CGRect(x: 0, y: 0,width: 200,height: 20)
self.addSubview(l1!)
l2?.frame = CGRect(x: 0, y: 25,width: 200,height: 20)
self.addSubview(l2!)
}
override func loadData(_ obj : Any){
let ds = obj as! DoubleString
l1?.text = ds.s1
l2?.text = ds.s2
}
}
class StringCell:Cell{
override func loadData(_ obj : Any){
textLabel?.text = "\(obj)"
}
}
// Framework Zone
class Table: UITableView,UITableViewDataSource,UITableViewDelegate{
var ds_ : [[Any]]?
public var ds : [[Any]]?{
get{return ds_}
set{
ds_ = newValue
}
}
var cellClass_ : AnyClass?
public var cellClass : AnyClass?{
get{return cellClass_}
set{cellClass_ = newValue}
}
func load(){
self.dataSource = self
self.delegate = self
register( cellClass, forCellReuseIdentifier: "\(cellClass)");
print("\(cellClass)")
}
func numberOfSections(in: UITableView) -> Int {
return ds!.count
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ds![section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let arr = ds!
let a = dequeueReusableCell(withIdentifier: "\(cellClass)", for: indexPath) ;
loadCell(a,arr[indexPath.section][indexPath.row])
return a
}
func loadCell(_ cell : UITableViewCell,_ item : Any){
(cell as! Cell).loadData(item)
}
}
class Cell : UITableViewCell{
public func loadData(_ obj : Any){
textLabel?.text = "implements yourself cell for \(obj)"
}
}複製代碼
繼承了Cell,而且實現了loadData,就能夠在loadData內本身加載任何傳入進來的數據項對象,這裏的數據項對象是DoubleString,內部承載了兩個字符串。固然還能夠是任何類型的對象,反正在本身的loadData內本身編寫加載數據項對象就能夠了。ide
固然,這段代碼依然並不理想,由於loadData的實現,依然要求開發者本身覆蓋基礎類Cell的loadData。這樣的耦合並不討喜。最好是此處的函數也能夠參數化,方法就是讓開發者本身傳遞一個事件函數進來,須要時,框架調用用戶的事件函數:函數
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
let page = Page()
page.view.backgroundColor = .blue
self.window!.rootViewController = page
self.window?.makeKeyAndVisible()
return true
}
}
class Page: UIViewController {
var a : Table!
override func viewDidLoad() {
super.viewDidLoad()
var s = DoubleString("Paris","Charles de Gaulle")
var t = DoubleString("Rome","FCO")
a = Table()
a.onCellData = {(cell ,obj) in
let ds = obj as! DoubleString
let c = cell as! DoubleStringCell
c.l1?.text = ds.s1
c.l2?.text = ds.s2
}
a.cellClass = DoubleStringCell.self
a.ds = [[s,t]]
a.load()
a.frame = CGRect(x: 0,y: 50,width: 300,height: 500)
self.view.addSubview(a)
}
}
class DoubleString{
var s1 : String
var s2 : String
init(_ s1:String,_ s2:String){
self.s1=s1
self.s2=s2
}
}
class DoubleStringCell:UITableViewCell{
var l1 : UILabel?=UILabel()
var l2 : UILabel?=UILabel()
override func layoutSubviews() {
l1?.frame = CGRect(x: 0, y: 0,width: 200,height: 20)
self.addSubview(l1!)
l2?.frame = CGRect(x: 0, y: 25,width: 200,height: 20)
self.addSubview(l2!)
}
}
// Framework Zone
typealias OnCellData = (_ cell : UITableViewCell ,_ obj : Any)->Void
class Table: UITableView,UITableViewDataSource,UITableViewDelegate{
var ds_ : [[Any]]?
var onCellData_ : OnCellData?
var onCellData : OnCellData?{
get{return onCellData_}
set{
onCellData_ = newValue
}
}
public var ds : [[Any]]?{
get{return ds_}
set{
ds_ = newValue
}
}
var cellClass_ : AnyClass?
public var cellClass : AnyClass?{
get{return cellClass_}
set{cellClass_ = newValue}
}
func load(){
self.dataSource = self
self.delegate = self
register( cellClass, forCellReuseIdentifier: "\(cellClass)");
print("\(cellClass)")
}
func numberOfSections(in: UITableView) -> Int {
return ds!.count
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 44
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ds![section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let arr = ds!
let a = dequeueReusableCell(withIdentifier: "\(cellClass)", for: indexPath) ;
loadCell(a,arr[indexPath.section][indexPath.row])
return a
}
func loadCell(_ cell : UITableViewCell,_ item : Any){
onCellData_?(cell,item)
}
}複製代碼
本來須要繼承並覆蓋的loadData如今能夠轉移爲事件,從而砍掉一些不自在的代碼。此案例中,還有一處能夠稍微優化,就是viewDidLoad內的DoubleString,喜好Swift的字面量對象的話,能夠改寫爲數組,反正解析的時候,轉換爲數組就好:優化
override func viewDidLoad() {
super.viewDidLoad()
a = Table()
a.onCellData = {(cell ,obj) in
let ds = obj as! [String]
let c = cell as! DoubleStringCell
c.l1?.text = ds[0]
c.l2?.text = ds[1]
}
a.cellClass = DoubleStringCell.self
a.ds = [[["Paris","Charles de Gaulle"],["Rome","FCO"]]]
a.load()
a.frame = CGRect(x: 0,y: 50,width: 300,height: 500)
self.view.addSubview(a)
}複製代碼