Swift(二十一)UITableView

更新:2018.05.24

整理了一下demo:SwiftDemo


最近比較忙,沒(méi)什么時(shí)間寫(xiě),斷斷續(xù)續(xù)寫(xiě)一點(diǎn)克饶。

UITableView是我們開(kāi)發(fā)過(guò)程中比較常用的,用于顯示一系列對(duì)象咙俩,UITableView繼承自UIScrollViewUIScrollView可以在任意方向滑動(dòng)湿故,而UITableView只在垂直方向上滑動(dòng)阿趁。UITableView中的內(nèi)容是由UITableViewCell負(fù)責(zé)顯示的。

1. UITableViewCell

  • UITableViewUITableViewCell組成坛猪,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è)置。

  • indexPathNSIndexPath類用來(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ù)源里的valueselecrt,就把 accessoryType屬性設(shè)置成checkmark狱窘。

  • tableView(_:, didSelectRowAt:)方法中杜顺,可以看到,用For循環(huán)來(lái)修改元數(shù)據(jù)中選中狀態(tài)的value训柴,然后調(diào)用reloadData()方法刷新cell哑舒。

  • cellaccessoryType屬性的值是枚舉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)的哗魂。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市辙芍,隨后出現(xiàn)的幾起案子啡彬,更是在濱河造成了極大的恐慌,老刑警劉巖故硅,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庶灿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡吃衅,警方通過(guò)查閱死者的電腦和手機(jī)往踢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)徘层,“玉大人峻呕,你說(shuō)我怎么就攤上這事∪ばВ” “怎么了瘦癌?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)跷敬。 經(jīng)常有香客問(wèn)我讯私,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任斤寇,我火速辦了婚禮桶癣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘娘锁。我一直安慰自己牙寞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布莫秆。 她就那樣靜靜地躺著间雀,像睡著了一般。 火紅的嫁衣襯著肌膚如雪馏锡。 梳的紋絲不亂的頭發(fā)上雷蹂,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音杯道,去河邊找鬼匪煌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛党巾,可吹牛的內(nèi)容都是我干的萎庭。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼齿拂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼驳规!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起署海,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吗购,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后砸狞,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體捻勉,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年刀森,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了踱启。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡研底,死狀恐怖埠偿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情榜晦,我是刑警寧澤冠蒋,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站乾胶,受9級(jí)特大地震影響浊服,放射性物質(zhì)發(fā)生泄漏统屈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一牙躺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腕扶,春花似錦孽拷、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至窿侈,卻和暖如春炼幔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背史简。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工乃秀, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人圆兵。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓跺讯,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親殉农。 傳聞我的和親對(duì)象是個(gè)殘疾皇子刀脏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容