更新:2018.05.24
整理了一下demo:SwiftDemo
最近比較忙,沒(méi)什么時(shí)間寫(xiě),斷斷續(xù)續(xù)寫(xiě)一點(diǎn)克饶。
UITableView是我們開(kāi)發(fā)過(guò)程中比較常用的,用于顯示一系列對(duì)象咙俩,UITableView繼承自UIScrollView,UIScrollView可以在任意方向滑動(dòng)湿故,而UITableView只在垂直方向上滑動(dòng)阿趁。UITableView中的內(nèi)容是由UITableViewCell負(fù)責(zé)顯示的。
1. UITableViewCell
-
UITableView
由UITableViewCell
組成坛猪,UITabelViewCell
負(fù)責(zé)顯示數(shù)據(jù)脖阵。 -
UITableView
的每一行,即每一個(gè)UITableViewCell
顯示一條項(xiàng)目墅茉。 -
UITableViewCell
對(duì)象的數(shù)量不受限制命黔,僅由設(shè)備內(nèi)存決定呜呐。 -
UITableViewCell
類定義了單元格在UITableView
中的屬性和行為。
創(chuàng)建 UITableViewCell
的時(shí)候悍募,你可以自定義一個(gè) cell
蘑辑,或者使用系統(tǒng)預(yù)定義的幾種格式。系統(tǒng)預(yù)定義的 cell
提供了 textLabel
坠宴、detailTextLabel
屬性和imageView
屬性用來(lái)設(shè)置cell
的內(nèi)容和圖片洋魂。樣式由UITableViewCellStyle枚舉來(lái)控制:
枚舉類型 | 描述 |
---|---|
.default |
包含一個(gè)左側(cè)的可選圖像視圖,和一個(gè)左對(duì)齊的標(biāo)簽對(duì)象喜鼓。 |
.value1 |
包含一個(gè)左側(cè)的可選視圖和一個(gè)左對(duì)齊的標(biāo)簽對(duì)象副砍,在單元格右側(cè)還有一個(gè)灰色、右對(duì)齊的標(biāo)簽對(duì)象颠通。 |
.value2 |
包含一個(gè)左側(cè)址晕、右對(duì)齊的藍(lán)色文字標(biāo)簽對(duì)象和一個(gè)右側(cè)的左對(duì)齊的標(biāo)簽對(duì)象膀懈。 |
.subtitle |
包含一個(gè)左側(cè)的可選圖像視圖顿锰,和一個(gè)左對(duì)齊的標(biāo)簽對(duì)象,在這個(gè)標(biāo)簽對(duì)象下方启搂,還有一個(gè)字體較小的標(biāo)簽對(duì)象硼控。 |
2. 創(chuàng)建一個(gè)UITableView
創(chuàng)建UITableView,首先是實(shí)例化一個(gè)UITableView對(duì)象胳赌,還要涉及到它的代理UITabelViewDataSource牢撼、UITableViewDelegate,在UITableViewDataSource代理方法中定義UITableViewCell的樣式疑苫。
import UIKit
class ViewController:UIViewController,UITableViewDataSource,UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let tableView = UITableView(frame: view.bounds, style: .grouped)
tableView.backgroundColor = UIColor.white;
view.addSubview(tableView)
tableView.dataSource = self
tableView.delegate = self
}
//MARK: UITableViewDataSource
// cell的個(gè)數(shù)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
// UITableViewCell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellid = "testCellID"
var cell = tableView.dequeueReusableCell(withIdentifier: cellid)
if cell==nil {
cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellid)
}
cell?.textLabel?.text = "這個(gè)是標(biāo)題~"
cell?.detailTextLabel?.text = "這里是內(nèi)容了油~"
cell?.imageView?.image = UIImage(named:"Expense_success")
return cell!
}
//MARK: UITableViewDelegate
// 設(shè)置cell高度
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 44.0
}
// 選中cell后執(zhí)行此方法
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
}
}
-
添加了代理協(xié)議UITableViewCell熏版,主要用來(lái)給UITableView提供數(shù)據(jù)來(lái)源,并用來(lái)處理數(shù)據(jù)源的變化捍掺。
它的主要帶你方法:-
tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
:
初始化和復(fù)用指定索引位置的UITableViewCell撼短,必須實(shí)現(xiàn)。 -
tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)
:
設(shè)置某一章節(jié)(section
)中的單元格數(shù)量挺勿,必須實(shí)現(xiàn)曲横。 -
numberOfSections(in tableView: UITableView)
:
設(shè)置表格中的章節(jié)(section
)個(gè)數(shù)。 -
tableView(_ tableView: UITableView, titleForHeaderInSection section: Int)
:
設(shè)置指定章節(jié)的標(biāo)題文字不瓶,如果不設(shè)置或代理返回值為nil禾嫉,不顯示。 -
tableView(_ tableView: UITableView, titleForFooterInSection section: Int)
:
設(shè)置章節(jié)腳部標(biāo)題文字蚊丐,如果不設(shè)置或代理返回值為nil熙参,不顯示。 -
tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath)
:
設(shè)置表格中指定索引位置的cell
是否可編輯麦备,可編輯的cell
會(huì)顯示插入和刪除的圖標(biāo)孽椰。 -
tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
:
當(dāng)完成插入或刪除操作時(shí)會(huì)調(diào)用此方法讲竿。 -
tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath)
:
設(shè)置指定索引位置的cell
是否可以通過(guò)拖動(dòng)的方式,改變它的位置弄屡。 -
tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)
:
當(dāng)cell
從一個(gè)位置拖動(dòng)到另一個(gè)位置時(shí)調(diào)用此方法题禀。
-
然后我們進(jìn)行了實(shí)例化,設(shè)置位置和尺寸膀捷,然后設(shè)置UITableView的數(shù)據(jù)源為當(dāng)前視圖控制器對(duì)象迈嘹,即設(shè)置代理UITableViewDataSource。
實(shí)現(xiàn)數(shù)據(jù)源協(xié)議定義中的方法全庸,從而設(shè)置章節(jié)中
cell
的個(gè)數(shù)秀仲,以及對(duì)cell
進(jìn)行初始化和復(fù)用設(shè)置。indexPath:NSIndexPath類用來(lái)描述在嵌套數(shù)列的樹(shù)種指定節(jié)點(diǎn)的路徑壶笼,即索引路徑神僵。索引路徑中的每一個(gè)索引,都用來(lái)表示一個(gè)節(jié)點(diǎn)的子數(shù)組中的指定索引位置覆劈。事實(shí)上保礼,NSIndexPath描述了一整個(gè)數(shù)列,表示在表格視圖中指定的章節(jié)中的指定行责语。
UITableView中的索引路徑包括兩個(gè)元素炮障,第一個(gè)元素section是表格的章節(jié)序號(hào),第二個(gè)元素row表示章節(jié)中的行序號(hào)坤候。-
還添加了UITableViewDelegate代理協(xié)議胁赢,它的主要作用是提供一些可選的方法,用來(lái)控制表格的選擇白筹、指定章節(jié)的頭和尾的顯示智末、單元格內(nèi)容的復(fù)制和粘貼以及協(xié)助完成單元格的排序等功能。
主要代理方法有:-
tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath)
:
設(shè)置單元格高度徒河,每當(dāng)表格需要顯示時(shí)系馆,都會(huì)調(diào)用此方法。 -
tableView(_ tableView: UITableView, heightForHeaderInSection section: Int)
:
設(shè)置某一索引下的章節(jié)頭部的高度虚青。 -
tableView(_ tableView: UITableView, heightForFooterInSection section: Int)
:
設(shè)置某一索引下的章節(jié)尾部的高度它呀。 -
tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
:
當(dāng)指定索引位置上的單元格即將顯示時(shí),調(diào)用此方法棒厘。此方法是委托對(duì)象有機(jī)會(huì)在單元格顯示之前重寫(xiě)其狀態(tài)屬性纵穿,如背景顏色等。 -
tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
:
當(dāng)用戶點(diǎn)擊選擇指定索引位置的單元格時(shí)奢人,調(diào)用此方法谓媒。 -
tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath)
:
當(dāng)用戶點(diǎn)擊一個(gè)已經(jīng)被選中的單元格時(shí),調(diào)用此方法何乎。
-
3. UITableView 復(fù)用機(jī)制
復(fù)用機(jī)制在很多地方都有應(yīng)用句惯,比如去飯店吃飯土辩,盤(pán)子、碗都不是一次性的抢野,當(dāng)客人使用過(guò)之后拷淘,會(huì)經(jīng)過(guò)消毒、清洗指孤。然后再給下一批客人使用启涯。如果每個(gè)客人使用過(guò)之后都換一批新的話,那成本太高了恃轩。
UITableView也采用復(fù)用機(jī)制结洼,一個(gè)UITableView可能需要顯示100條數(shù)據(jù),但屏幕尺寸有限叉跛,假設(shè)一次只能顯示9條松忍,如果滑動(dòng)一下的話,會(huì)顯示出第10條的一部分筷厘,所以當(dāng)前屏幕在這種情況下最多只能顯示出10條鸣峭。
所以系統(tǒng)只需要?jiǎng)?chuàng)建10個(gè)UITableViewCell對(duì)象就好,當(dāng)手指從下往上滑動(dòng)時(shí)敞掘,回收處于屏幕之外的最上方單元格叽掘,并放置到表格最下方,作為將要顯示的11個(gè)單元格玖雁。當(dāng)UITableView對(duì)象從上往下滑動(dòng)時(shí),也是同樣的服用機(jī)制盖腕。
在上面的代碼中:
let cellid = "testCellID"
var cell = tableView.dequeueReusableCell(withIdentifier: cellid)
dequeueReusableCell
方法的作用是從單元格對(duì)象池中獲取指定類型并可復(fù)用的單元格對(duì)象赫冬。
if cell==nil {
cell = UITableViewCell(style: .subtitle, reuseIdentifier: cellid)
}
如果從對(duì)象池中沒(méi)有獲得可復(fù)用的單元格,就調(diào)用實(shí)例化方法實(shí)例一個(gè)某一類型的溃列、可復(fù)用的單元格劲厌。
-
style
參數(shù): 枚舉常量,用于表示單元格的樣式听隐。 -
reuseIdentifier
: 作為一個(gè)字符串類型的參數(shù)补鼻,它用來(lái)標(biāo)識(shí)具有相同類型的、可復(fù)用的單元格雅任。對(duì)于相同類型的單元格风范,需要使用相同的reuseIdentifier
參數(shù)。
4. 自定義UITableViewCell
一般對(duì)于相對(duì)復(fù)雜一些的顯示內(nèi)容沪么,我們會(huì)創(chuàng)建一個(gè)UITableViewCell的類文件硼婿。
Subclass of 寫(xiě)UITableViewCell
上代碼:
import UIKit
class NewTableViewCell: UITableViewCell {
let width:CGFloat = UIScreen.main.bounds.width
var userLabel:UILabel! // 名字
var birthdayLabel:UILabel! // 出生日期
var sexLabel:UILabel! // 性別
var iconImv:UIImageView! // 頭像
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
// 頭像
iconImv = UIImageView(frame: CGRect(x: 20, y: 15, width: 44, height: 44))
iconImv.layer.masksToBounds = true
iconImv.layer.cornerRadius = 22.0
// 名字
userLabel = UILabel(frame: CGRect(x: 74, y: 18, width: 70, height: 15))
userLabel.textColor = UIColor.black
userLabel.font = UIFont.boldSystemFont(ofSize: 15)
userLabel.textAlignment = .left
// 性別
sexLabel = UILabel(frame: CGRect(x: 150, y: 20, width: 50, height: 13))
sexLabel.textColor = UIColor.black
sexLabel.font = UIFont.systemFont(ofSize: 13)
sexLabel.textAlignment = .left
// 出生日期
birthdayLabel = UILabel(frame: CGRect(x: 74, y: 49, width: width-94, height: 13))
birthdayLabel.textColor = UIColor.gray
birthdayLabel.font = UIFont.systemFont(ofSize: 13)
birthdayLabel.textAlignment = .left
contentView.addSubview(iconImv)
contentView.addSubview(userLabel)
contentView.addSubview(sexLabel)
contentView.addSubview(birthdayLabel)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
- 上面代碼中,首先給NewTableViewCell添加了五個(gè)屬性:
width
屏幕寬度禽车、iconImv
頭像寇漫、userLabel
用戶名刊殉、sexLabel
性別和birthdayLabel
生日。 - 然后添加實(shí)例化方法:
init(style: UITableViewCellStyle, reuseIdentifier: String?)
并在方法中實(shí)例化定義的4個(gè)屬性州胳,將他們添加到屏幕上记焊。 - 最后實(shí)現(xiàn)繼承自UITableViewCell類所必須的
init?(coder aDecoder: NSCoder)
構(gòu)造函數(shù)。
現(xiàn)在我們完成了NewTableViewCell的創(chuàng)建栓撞,再到ViewController.swift類文件中亚亲,調(diào)用這個(gè)自定義單元格類。
import UIKit
class ViewController:UIViewController,UITableViewDataSource,UITableViewDelegate {
var dataSource = [[String:String]()]
override func viewDidLoad() {
super.viewDidLoad()
let tableView = UITableView(frame: view.bounds, style: .grouped)
tableView.backgroundColor = UIColor.white;
view.addSubview(tableView)
tableView.dataSource = self
tableView.delegate = self
dataSource = [
["name":"王小明","sex":"男","icon":"my_def_photo","birthday":"2017-10-11"],
["name":"李磊","sex":"男","icon":"my_def_photo","birthday":"2011-12-30"],
["name":"韓梅","sex":"女","icon":"my_def_photo","birthday":"2014-9-10"],
["name":"JIM","sex":"男","icon":"my_def_photo","birthday":"2008-10-1"]]
tableView.reloadData()
}
//MARK: UITableViewDataSource
// cell的個(gè)數(shù)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
// UITableViewCell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellid = "testCellID"
var cell:NewTableViewCell? = tableView.dequeueReusableCell(withIdentifier: cellid) as? NewTableViewCell
if cell==nil {
cell = NewTableViewCell(style: .subtitle, reuseIdentifier: cellid)
}
let dict:Dictionary = dataSource[indexPath.row]
cell?.iconImv.image = UIImage(named: dict["icon"]!)
cell?.userLabel.text = dict["name"]
cell?.sexLabel.text = dict["sex"]
cell?.birthdayLabel.text = dict["birthday"]
return cell!
}
//MARK: UITableViewDelegate
// 設(shè)置cell高度
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 74.0
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 20
}
// 選中cell后執(zhí)行此方法
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
}
}
- 這里實(shí)例了一個(gè)數(shù)組腐缤,數(shù)組內(nèi)的元素是字典捌归,用來(lái)存放需要展示的數(shù)據(jù)。
- 然后注意
tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)
代理方法岭粤,返回參數(shù)是dataSource.count
惜索,意思是數(shù)組中有幾條數(shù)據(jù)就展示幾個(gè)Cell。 - 接下來(lái)就是修改
tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
方法中的Cell了剃浇。并根據(jù)dataSource
數(shù)組中的數(shù)據(jù)對(duì)cell的元素進(jìn)行賦值巾兆。 - 后面我們還修改了cell的高度,和header的高度虎囚。跑一下項(xiàng)目:
5. 添加索引和章節(jié)(Section)
最常見(jiàn)的帶有索引的TableView就是通訊錄了吧角塑,在TableView的右側(cè)有一個(gè)垂直的索引序列,點(diǎn)擊索引序列的元素可在表格中迅速定位到指定的位置淘讥,尤其是擁有大量數(shù)據(jù)的時(shí)候圃伶。
先來(lái)看一下索引需要用到的代理方法:
-
numberOfSections(in tableView: UITableView)
設(shè)置TableView中章節(jié)(Section的數(shù)量)不設(shè)置默認(rèn)為1。 -
tableView(_ tableView: UITableView, numberOfRowsInSection section: Int)
在指定章節(jié)中蒲列,cell的個(gè)數(shù)窒朋。
-tableView(_ tableView: UITableView, titleForHeaderInSection section: Int)
設(shè)置章節(jié)標(biāo)題文字,返回結(jié)果為字符串蝗岖,如果返回為nil侥猩,則不顯示標(biāo)題。 -
sectionIndexTitles(for tableView: UITableView)
設(shè)置在表格右側(cè)顯示的索引序列的內(nèi)容抵赢,返回結(jié)果為一個(gè)字符串?dāng)?shù)組 -
tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
TableViewCell初始化和復(fù)用
開(kāi)始之前欺劳,需要先創(chuàng)建索引表格所需的數(shù)據(jù)源,剛才的例子中是添加了一個(gè) 數(shù)組 作為數(shù)據(jù)源铅鲤,這里索引的話需要一個(gè) 字典 來(lái)作為數(shù)據(jù)源划提。
開(kāi)發(fā)中需要數(shù)據(jù)源通常各種各樣,不如加載本地文本文件和plist文件彩匕,或者從服務(wù)器請(qǐng)求書(shū)院腔剂,通過(guò)返回的JSON或XML作為數(shù)據(jù)源。這里我們僅創(chuàng)建一個(gè)字典作為數(shù)據(jù)源驼仪。
代碼搞完了掸犬,上代碼:
import UIKit
class IndexsViewController: UIViewController,UITableViewDataSource {
let contents:Dictionary<String,[String]> =
["A":["安其拉"],
"B":["步驚云","不知火舞","白起","扁鵲"],
"C":["程咬金","成吉思汗","蔡文姬","曹操"],
"D":["妲己","狄仁杰","典韋","貂蟬","達(dá)摩","大喬","東皇太一"],
"G":["高漸離","關(guān)羽","宮本武藏","干將莫邪","鬼谷子"],
"H":["韓信","后羿","花木蘭","黃忠"],
"J":["荊軻","姜子牙"],
"L":["老夫子","劉邦","劉嬋","魯班七號(hào)","蘭陵王","露娜","廉頗","李元芳","劉備","李白","呂布"],
"M":["墨子","羋月"],
"N":["牛魔","娜可露露","哪吒","女?huà)z"],
"P":["龐統(tǒng)",""],
"S":["孫臏","孫尚香","孫悟空"],
"W":["王昭君","武則天"],
"X":["項(xiàng)羽","小喬"],
"Y":["亞瑟","虞姬","嬴政"],
"Z":["周瑜","莊周","甄姬","鐘無(wú)艷","張飛","張良","鐘馗","趙云","諸葛亮"]]
var keys:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.isNavigationBarHidden = false
// 把字典里的key拿出來(lái)放到一個(gè)數(shù)組中袜漩,備用,作為章節(jié)的標(biāo)題
keys = contents.keys.sorted()
let tableView = UITableView(frame: view.bounds, style: .plain)
tableView.dataSource = self
view.addSubview(tableView)
}
//MARK: UITableViewDataSource
//MARK: 章節(jié)的個(gè)數(shù)
func numberOfSections(in tableView: UITableView) -> Int {
return keys.count
}
//MARK: 某一章節(jié)cell個(gè)數(shù)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let arr = contents[keys[section]]
return (arr?.count)!
}
//MARK: 初始化cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "indexsCellId")
if cell==nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "indexsCellId")
}
let arr = contents[keys[indexPath.section]]
cell?.textLabel?.text = arr?[indexPath.row]
return cell!
}
//MARK: 每一個(gè)章節(jié)的標(biāo)題
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return keys[section]
}
//MARK: 設(shè)置索引序列內(nèi)容
func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return keys
}
}
- 首先我們來(lái)確定一下數(shù)據(jù)源湾碎,本來(lái)是想用人名的宙攻,不過(guò)想想萬(wàn)一暴露了什么被發(fā)現(xiàn)~~,然后就用了農(nóng)藥里的英雄介褥,勞逸結(jié)合座掘,勞逸結(jié)合。這不是重點(diǎn)
- 然后我們定義了一個(gè)數(shù)組
keys
柔滔,用來(lái)存放數(shù)據(jù)源里面的key
溢陪。 - 實(shí)例化TableView,設(shè)置代理睛廊,實(shí)現(xiàn)需要用到的代理方法形真。這還不是重點(diǎn)。
- 實(shí)現(xiàn)代理方法超全,都有注釋咆霜,就不細(xì)說(shuō)了,設(shè)置章節(jié)個(gè)數(shù)嘶朱、設(shè)置每個(gè)章節(jié)的cell個(gè)數(shù)蛾坯,初始化cell、設(shè)置每一個(gè)章節(jié)的頭部標(biāo)題疏遏。這也不是重點(diǎn)
- 然后實(shí)現(xiàn)代理脉课,設(shè)置索引內(nèi)容:
sectionIndexTitles(for tableView: UITableView)
,這才是重點(diǎn)改览,添加了這個(gè)方法下翎,右側(cè)才會(huì)出現(xiàn)索引序列。
點(diǎn)擊索引條目宝当,會(huì)迅速的到達(dá)點(diǎn)擊索引內(nèi)容的部分。
需要注意的是: 實(shí)例化的時(shí)候胆萧,init
方法第二個(gè)參數(shù)有兩個(gè)值:.plain
和.grouped
庆揩。
如果不添加章節(jié)頭部的話,基本看不出這兩個(gè)值給tableView帶來(lái)的變化跌穗。
但在這里订晌,是有區(qū)別的:
-
.plain
:如果傳的是這個(gè)參數(shù),向上滑動(dòng)蚌吸,當(dāng)章節(jié)頭部滑動(dòng)到UITableVeiw
的上方邊界時(shí)锈拨,章節(jié)頭部會(huì)停在邊界位置,知道下一個(gè)章節(jié)頭部到達(dá)它的位置羹唠,它才會(huì)繼續(xù)向上滑動(dòng)奕枢,下一個(gè)章節(jié)頭部會(huì)占據(jù)它的位置娄昆。 -
. grouped
:就正常滑動(dòng)缝彬,沒(méi)啥影響萌焰。
哦,除了這個(gè)還是有別的區(qū)別的谷浅,當(dāng)設(shè)置的是plain
扒俯,如果cell的個(gè)數(shù)不夠撲滿屏幕,系統(tǒng)會(huì)一直創(chuàng)建空的cell來(lái)?yè)錆M一疯,可以試一下撼玄,能看到一條一條的橫線,cell的分割線墩邀,如果是設(shè)置的grouped
就不會(huì)有這種情況掌猛。
上張圖:
6. cell的選擇和取消選擇
本來(lái)想等著看WWDC的,結(jié)果睡著了磕蒲,早上看了新聞留潦,又特么要做適配了,還特么這么貴辣往。
這個(gè)有很多場(chǎng)景會(huì)遇到的兔院,比如說(shuō),我們之前項(xiàng)目里有支付功能站削,需要設(shè)置一下默認(rèn)支付方式坊萝,默認(rèn)微信還是支付寶,產(chǎn)品給的UI就是一表格许起。
需求:三種支付方式十偶,只能單選。
import UIKit
class SelectViewController: UIViewController,UITableViewDataSource,UITableViewDelegate {
// 數(shù)據(jù)源园细,
var dataSource = [["微信支付":"select"],["支付寶支付":"on"],["銀聯(lián)支付":"no"]]
override func viewDidLoad() {
super.viewDidLoad()
let tableView = UITableView(frame: view.bounds, style: .grouped)
tableView.backgroundColor = UIColor.white
tableView.delegate = self
tableView.dataSource = self
view.addSubview(tableView)
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0.01
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "selectCell")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "selectCell")
cell?.selectionStyle = .none
}
let dic = dataSource[indexPath.row] as Dictionary
cell?.textLabel?.text = dic.keys.first
if dic.values.first == "select" {
cell?.accessoryType = .checkmark
} else {
cell?.accessoryType = .none
}
return cell!
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var i = 0
for var dict in dataSource {
if i == indexPath.row {
dict[dict.keys.first!] = "select"
dataSource[i] = dict
} else {
dict[dict.keys.first!] = "no"
dataSource[i] = dict
}
i = i+1
}
tableView.reloadData()
}
}
先說(shuō)一下思路:首先定義了一個(gè)數(shù)據(jù)源惦积,因?yàn)槭菃芜x,所以用的是數(shù)組嵌套字典猛频,key就是支付方式狮崩,value是代表是否選中個(gè)狀態(tài)的string,如果選中鹿寻,就把數(shù)據(jù)源里這個(gè)位置的value變成select睦柴,但是只能單選,所以還需要把其他的都變成no毡熏。
直接說(shuō)重點(diǎn):cell的實(shí)例化和復(fù)用代理方法中坦敌,可以看到,如果數(shù)據(jù)源里的value是
selecrt
,就把accessoryType
屬性設(shè)置成checkmark
狱窘。tableView(_:, didSelectRowAt:)
方法中杜顺,可以看到,用For
循環(huán)來(lái)修改元數(shù)據(jù)中選中狀態(tài)的value训柴,然后調(diào)用reloadData()
方法刷新cell哑舒。cell
的accessoryType
屬性的值是枚舉UITableViewCellAccessoryType:
枚舉類型 | 說(shuō)明 |
---|---|
none |
沒(méi)有任何的樣式 |
detailButton |
右側(cè)藍(lán)色的圓圈,中間帶嘆號(hào) |
detailDisclosureButton |
右側(cè)藍(lán)色圓圈帶嘆號(hào)幻馁,它的右側(cè)還有一個(gè)灰色向右的箭頭 |
disclosureIndicator |
右側(cè)一個(gè)灰色的向右箭頭 |
checkmark |
右側(cè)藍(lán)色對(duì)號(hào) |
7. cell的插入和刪除
插入和刪除設(shè)計(jì)到的兩個(gè)代理方法:
-
tableView(_ tableView:, editingStyleForRowAt indexPath:)
確定編輯模式洗鸵,Add or Delete -
tableView(_ tableView:, commit editingStyle:, forRowAt indexPath:)
當(dāng)執(zhí)行編輯操作時(shí),調(diào)用此方法
和一個(gè)開(kāi)啟TableView編輯模式的方法:
-
setEditing(_ editing:, animated:)
-
editing
: 是否開(kāi)啟編輯狀態(tài) -
animated
: 是否有動(dòng)畫(huà)效果
-
import UIKit
class AddViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
var dataSource = [["微信","支付寶","銀聯(lián)"],["微信","支付寶","銀聯(lián)"]]
var tableView:UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.title = "支付方式"
let rightBar = UIBarButtonItem.init(barButtonSystemItem: .add, target: self, action: #selector(addButtonClick))
navigationItem.rightBarButtonItem = rightBar
tableView = UITableView(frame: view.bounds, style: .grouped)
tableView.delegate = self
tableView.dataSource = self
view.addSubview(tableView)
}
//MARK: 導(dǎo)航欄右側(cè)按鈕仗嗦,點(diǎn)擊開(kāi)啟或關(guān)閉編輯模式
func addButtonClick() {
tableView.setEditing(!tableView.isEditing, animated: true)
}
func numberOfSections(in tableView: UITableView) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "addCell")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "addCell")
cell?.selectionStyle = .none
}
let arr = dataSource[indexPath.section] as Array
cell?.textLabel?.text = arr[indexPath.row] as String
return cell!
}
//MARK: 編輯模式膘滨,增加還是刪除
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
if indexPath.section == 1 {
return .delete
}
return .insert
}
//MARK: 執(zhí)行編輯操作時(shí),調(diào)用此方法
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
var arr = dataSource[indexPath.section] as Array
if editingStyle == .insert {
arr.insert("Apple Pay", at: indexPath.row)
dataSource[indexPath.section] = arr
tableView.insertRows(at: [indexPath], with: .right)
} else {
arr.remove(at: indexPath.row)
dataSource[indexPath.section] = arr
tableView.deleteRows(at: [indexPath], with: .left)
}
}
}
- 說(shuō)下思路稀拐,為了方便舉例火邓,我設(shè)置了兩個(gè)Section,第一個(gè)Section做增加操作德撬,第二個(gè)Section做刪除操作铲咨,所以數(shù)據(jù)源里放的是兩個(gè)數(shù)組。
- 在導(dǎo)航條右側(cè)添加了一個(gè)按鈕蜓洪,用來(lái)確定編輯狀態(tài)是否開(kāi)啟纤勒。
-
setEditing
方法上面說(shuō)過(guò)了,就不說(shuō)了隆檀∫√欤看下面兩個(gè)代理方法。 -
tableView(_: ,editingStyleForRowAt:)
方法返回參數(shù)是UITableViewCellEditingStyle:-
insert
: 添加操作 -
delete
: 刪除操作 -
none
: 沒(méi)有任何操作
-
-
tableView(_:, commit editingStyle:, forRowAt:)
恐仑,當(dāng)執(zhí)行了編輯操作泉坐,就會(huì)調(diào)起這個(gè)方法,你可以通過(guò)編輯狀態(tài)對(duì)TableView和數(shù)據(jù)源進(jìn)行操作裳仆。注意一定要把數(shù)據(jù)源和視圖顯示操作保持一致腕让,不然很容易數(shù)組越界導(dǎo)致崩潰。 -
insertRows(at indexPaths: [IndexPath], with animation: UITableViewRowAnimation)
和deleteRows(at indexPaths: [IndexPath], with animation: UITableViewRowAnimation)
方法的作用都是對(duì)TableViewCell的條數(shù)進(jìn)行操作歧斟,一個(gè)增加一個(gè)刪除 -
UITableViewRowAnimation
枚舉的作用是控制操作的動(dòng)畫(huà):
屬性 | 說(shuō)明 |
---|---|
fade |
以淡入淡出的方式顯示或移除 |
right |
添加或刪除時(shí)记某,從右側(cè)滑入或劃出 |
left |
添加或刪除時(shí),從左側(cè)滑入或劃出 |
top |
添加或刪除時(shí)构捡,從上方滑入或劃出 |
bottom |
添加或刪除時(shí),從底部滑入或劃出 |
middle |
表格視圖將盡量使新舊cell居中顯示在曾經(jīng)或?qū)⒁@示的位置 |
automatic |
自動(dòng)選擇適合自身的動(dòng)畫(huà)方式 |
none |
采用默認(rèn)動(dòng)畫(huà)方式 |
8. cell位置移動(dòng)功能
支持重新排序(Reordering)功能的TableView壳猜,允許用戶拖動(dòng)位于單元格右側(cè)的排序圖標(biāo)勾徽,來(lái)重新排序TableView中的單元格。
排序功能一般用到的還是比較多的统扳,我曾經(jīng)做過(guò)一個(gè)類似PPT的功能喘帚,要求可以更換演示版頁(yè)的排列方式畅姊,就是用的這個(gè)功能。
移動(dòng)功能同樣設(shè)計(jì)到了兩個(gè)代理方法:
-
tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath)
設(shè)置cell是否可移動(dòng) -
tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)
每次移動(dòng)結(jié)束后會(huì)調(diào)用此方法
移動(dòng)功能同樣需要開(kāi)啟編輯模式setEditing(_: ,animated:)
import UIKit
class ReorderViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
var dataSource = ["微信","支付寶","銀聯(lián)","易寶"]
var tableView:UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let rightBar = UIBarButtonItem.init(barButtonSystemItem: .add, target: self, action: #selector(ReorderButtonClick))
navigationItem.rightBarButtonItem = rightBar
tableView = UITableView(frame: view.bounds, style: .grouped)
tableView.delegate = self
tableView.dataSource = self
view.addSubview(tableView)
}
func ReorderButtonClick() {
tableView.setEditing(!tableView.isEditing, animated: true)
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 0.01
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "addCell")
if cell == nil {
cell = UITableViewCell(style: .default, reuseIdentifier: "addCell")
cell?.selectionStyle = .none
}
cell?.textLabel?.text = dataSource[indexPath.row] as String
return cell!
}
//MARK: 選擇編輯模式吹由,不刪除也不添加就設(shè)置為none
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return .none
}
//MARK: 設(shè)置cell是否可移動(dòng)
func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
return true
}
//MARK: 移動(dòng)結(jié)束后調(diào)用此方法
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let data = dataSource[sourceIndexPath.row]
dataSource.remove(at: sourceIndexPath.row)
dataSource.insert(data, at: destinationIndexPath.row)
}
}
- 說(shuō)下思路若未,先開(kāi)啟TableView的編輯模式,但我們現(xiàn)在不需要添加或刪除倾鲫,只需要移動(dòng)功能粗合。 直接看后面三個(gè)代理方法:
-
tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath)
:
因?yàn)槲覀儾恍枰砑踊騽h除操作,所以調(diào)用此方法乌昔,并設(shè)置返回none
-
tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath)
:
移動(dòng)返回true
-
tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)
:
主要說(shuō)這個(gè)隙疚,這個(gè)方法的作用是移動(dòng)結(jié)束后調(diào)用,一般我們用它來(lái)同步數(shù)據(jù)源中的數(shù)據(jù)保持與視圖同步磕道,從代碼里可以看出供屉,三個(gè)參數(shù)的作用:- 第一個(gè)就是移動(dòng)的tableView了,如果當(dāng)前視圖只有一個(gè)tableView不用管他溺蕉。
- 第二個(gè)參數(shù)是移動(dòng)的cell曾經(jīng)的位置伶丐。
- 第三個(gè)參數(shù)是移動(dòng)的最后位置。
沒(méi)有PS工具疯特,我是把圖片放到word里面截圖出來(lái)的哗魂。