Swift實(shí)現(xiàn)表格視圖單元格單選(2)
本文實(shí)例為大家分享了Swift實(shí)現(xiàn)表格視圖單元格單選的具體代碼,供大家參考,具體內(nèi)容如下
效果

前言
前段時間寫了一篇博客: 表格視圖單元格單選(一),實(shí)現(xiàn)起來并不復(fù)雜,簡單易懂。在實(shí)際開發(fā)中,可能會涉及到更為復(fù)雜的操作,比如多個section 下的單選,如上面展示的效果,當(dāng)我們有這樣的需求的時候,該如何實(shí)現(xiàn)呢?因為,在上篇文章中我所用的控件都是單元格自帶的imageView以及textLabel,本文我將主要分享自定義選擇按鈕以及在多個section下實(shí)現(xiàn)單選的方法。
準(zhǔn)備
界面搭建與數(shù)據(jù)顯示

這樣的界面相信對大家而言,并不難,這里我不再做詳細(xì)的講解,值得一提的是數(shù)據(jù)源的創(chuàng)建,每一組的頭部標(biāo)題,我用一個數(shù)組questions存儲,類型為:[String]?,由于每一組中,單元格內(nèi)容不一致,因此建議用字典存儲。如下所示:
var questions: [String]? var answers: ? [String:[String]]?
如果我用字典來存儲數(shù)據(jù),那字典的鍵我應(yīng)該如何賦值呢?其實(shí)很簡單,我們只需將section的值作為key 就Ok了,這樣做的好處在于,我可以根據(jù)用戶點(diǎn)擊的 section來處理對應(yīng)的數(shù)據(jù),我們知道,表格視圖的section 從 0 開始,因此字典賦值可以像下面提供的代碼一樣賦值,但要注意,answers的值需與questions里面的問題一致,才能滿足實(shí)際的需求。
self.questions = ["您的性別是:", ? ? ? ? ? ? ? ? ? "您意向工作地點(diǎn)是:", ? ? ? ? ? ? ? ? ? "您是否參加公司內(nèi)部培訓(xùn):"] self.answers = ["0":["男", "女"], ? ? ? ? ? ? ? ? "1":["成都", "上海", "北京", "深圳"], ? ? ? ? ? ? ? ? "2":["參加", "不參加","不確定"]]
接下來需要做的事情就是自定義單元格(UITableViewCell)了,比較簡單,直接上代碼,代碼中涉及到的圖片素材可到阿里矢量圖中下載:
import UIKit
class CustomTableViewCell: UITableViewCell {
? ? var choiceBtn: UIButton?
? ? var displayLab: UILabel?
? ? override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
? ? ? ? super.init(style: style, reuseIdentifier: reuseIdentifier)
? ? ? ? self.initializeUserInterface()
? ? }
? ? required init?(coder aDecoder: NSCoder) {
? ? ? ? fatalError("init(coder:) has not been implemented")
? ? }
? ? // MARK:Initialize methods
? ? func initializeUserInterface() {
? ? ? ? self.choiceBtn = {
? ? ? ? ? ? let choiceBtn = UIButton(type: UIButtonType.Custom)
? ? ? ? ? ? choiceBtn.bounds = CGRectMake(0, 0, 30, 30)
? ? ? ? ? ? choiceBtn.center = CGPointMake(20, 22)
? ? ? ? ? ? choiceBtn.setBackgroundImage(UIImage(named: "iconfont-select.png"), forState: UIControlState.Normal)
? ? ? ? ? ? choiceBtn.setBackgroundImage(UIImage(named: "iconfont-selected.png"), forState: UIControlState.Selected)
? ? ? ? ? ? choiceBtn.addTarget(self, action: Selector("respondsToButton:"), forControlEvents: UIControlEvents.TouchUpInside)
? ? ? ? ? ? return choiceBtn
? ? ? ? ? ? }()
? ? ? ? self.contentView.addSubview(self.choiceBtn!)
? ? ? ? self.displayLab = {
? ? ? ? ? ? let displayLab = UILabel()
? ? ? ? ? ? displayLab.bounds = CGRectMake(0, 0, 100, 30)
? ? ? ? ? ? displayLab.center = CGPointMake(CGRectGetMaxX(self.choiceBtn!.frame) + 60, CGRectGetMidY(self.choiceBtn!.frame))
? ? ? ? ? ? displayLab.textAlignment = NSTextAlignment.Left
? ? ? ? ? ? return displayLab
? ? ? ? ? ? }()
? ? ? ? self.contentView.addSubview(self.displayLab!)
? ? }
? ? // MARK:Events
? ? func respondsToButton(sender: UIButton) {
? ? }
}表格視圖數(shù)據(jù)源與代理的實(shí)現(xiàn),如下所示:
// MARK:UITableViewDataSource && UITableViewDelegate
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
? ? // 直接返回 answers 鍵值對個數(shù)即可,也可返回 questions 個數(shù);
? ? return (self.answers!.count)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
? ? // 根據(jù) section 獲取對應(yīng)的 key
? ? let key = "\(section)"
? ? // 根據(jù) key 獲取對應(yīng)的數(shù)據(jù)(數(shù)組)
? ? let answers = self.answers![key]
? ? // 直接返回數(shù)據(jù)條數(shù),就是需要的行數(shù)
? ? return answers!.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
? ? var cell: CustomTableViewCell? = tableView.dequeueReusableCellWithIdentifier("cell") as? CustomTableViewCell
? ? if cell == nil {
? ? ? ? cell = CustomTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
? ? }
? ? let key = "\(indexPath.section)"
? ? let answers = self.answers![key]
? ? cell!.selectionStyle = UITableViewCellSelectionStyle.None
? ? return cell!
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
? ? return 40
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
? ? return self.questions![section]
}實(shí)現(xiàn)
技術(shù)點(diǎn):在這里我主要會用到閉包回調(diào),在自定義的單元格中,用戶點(diǎn)擊按鈕觸發(fā)方法時,閉包函數(shù)會被調(diào)用,并將用戶點(diǎn)擊的單元格的indexPath進(jìn)行傳遞,然后根據(jù)indexPath進(jìn)行處理,具體的實(shí)現(xiàn)方式,下面會慢慢講到,閉包類似于Objective-C中的Block,有興趣的朋友可深入了解Swift中的閉包使用。
首先,我們需要在CustomTableViewCell.swift文件中,聲明一個閉包類型:
typealias IndexPathClosure = (indexPath: NSIndexPath) ->Void
其次,聲明一個閉包屬性:
var indexPathClosure: IndexPathClosure?
現(xiàn)在,要做的事情就是聲明一個閉包函數(shù)了,閉包函數(shù)主要用于在ViewController.swift文件中調(diào)用并且將需要傳遞的數(shù)據(jù)傳遞到ViewController.swift文件中。
func getIndexWithClosure(closure: IndexPathClosure?) {
? ? ? ? self.indexPathClosure = closure
? ? }閉包函數(shù)已經(jīng)有了,那么何時調(diào)用閉包函數(shù)呢?當(dāng)用戶點(diǎn)擊單元格的時候,閉包函數(shù)會被調(diào)用,因此,我們只需要到選擇按鈕觸發(fā)方法中去處理邏輯就好了,在觸發(fā)方法中,我們需要將單元格的indexPath屬性傳遞出去,但是,UITableViewCell并無indexPath屬性,那應(yīng)該怎么辦呢?我們可以為它創(chuàng)建一個indexPath屬性,在配置表格視圖協(xié)議方法cellForRowAtIndexPath:時,我們賦值單元格的indexPath屬性就OK了。
var indexPath: NSIndexPath?
func respondsToButton(sender: UIButton) {
? ? sender.selected = true
? ? if self.indexPathClosure != nil {
? ? ? ? self.indexPathClosure!(indexPath: self.indexPath!)
? ? }
}現(xiàn)在在CustomTableViewCell.swift文件里面的操作就差不多了,但是,還缺少一步,我還需要定制一個方法,用于設(shè)置按鈕的狀態(tài):
func setChecked(checked: Bool) {
? ? self.choiceBtn?.selected = checked
}到了這一步,我們要做的事情就是切換到ViewController.swift文件中,找到表格視圖協(xié)議方法cellForRowAtIndexPath:,主要的邏輯就在這個方法中處理,首先我們需要做的事情就是賦值自定義單元格的indexPath屬性:
cell?.indexPath = indexPath
其次,我需要在ViewController.swift文件中,聲明一個selectedIndexPath屬性用于記錄用戶當(dāng)前選中的單元格位置:
var selectedIndexPath: NSIndexPath?
接下來我會去做一個操作,判斷協(xié)議方法參數(shù)indexPath.row,是否與selectedIndexPath.row一致,如果一致,則設(shè)為選中,否則設(shè)為未選中,這里可用三目運(yùn)算符:
self.selectedIndexPath?.row == indexPath.row ? cell?.setChecked(true) : cell?.setChecked(false)
這里大家可能會有疑問,那就是為什么只判斷row呢?不用判斷section嗎?當(dāng)然不用,因為在刷新表格視圖的時候我并沒有調(diào)用reloadData方法,而是指定刷新某一組(section)就可以了,如果全部刷新,則無法保留上一組用戶選擇的信息,這將不是我們所需要的。
接下來,將是最后一步,調(diào)用回調(diào)方法,該方法會在每一次用戶點(diǎn)擊單元格的時候調(diào)用,并且返回用戶當(dāng)前點(diǎn)擊的單元格的indexPath,在這里,我們需要將返回的indexPath賦值給selectedIndexPath屬性。并且刷新指定section就OK了,代碼如下:
cell!.getIndexWithClosure { (indexPath) -> Void in
? ? self.selectedIndexPath = indexPath
? ? print("您選擇的答案是:\(answers![indexPath.row])")
? ? tableView.reloadSections(NSIndexSet(index: self.selectedIndexPath!.section), withRowAnimation: UITableViewRowAnimation.Automatic) ??
}完整代碼
可能大家還比較模糊,這里我將貼上完整的代碼供大家參考
ViewController.swift文件
import UIKit
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
? ? var tableView: UITableView?
? ? var questions: [String]?
? ? var answers: [String:[String]]?
? ? var selectedIndexPath: NSIndexPath?
? ? override func viewDidLoad() {
? ? ? ? super.viewDidLoad()
? ? ? ? self.initializeDatasource()
? ? ? ? self.initializeUserInterface()
? ? ? ? // Do any additional setup after loading the view, typically from a nib.
? ? }
? ? // MARK:Initialize methods
? ? func initializeDatasource() {
? ? ? ? self.questions = ["您的性別是:", "您意向工作地點(diǎn)是:", "您是否參加公司內(nèi)部培訓(xùn):"]
? ? ? ? self.answers = ["0":["男", "女"],
? ? ? ? ? ? ? ? ? ? ? ? "1":["成都", "上海", "北京", "深圳"],
? ? ? ? ? ? ? ? ? ? ? ? "2":["參加","不參加","不確定"]]
? ? }
? ? func initializeUserInterface() {
? ? ? ? self.title = "多組單選"
? ? ? ? self.automaticallyAdjustsScrollViewInsets = false
? ? ? ? // table view
? ? ? ? self.tableView = {
? ? ? ? ? ? let tableView = UITableView(frame: CGRectMake(0, 64, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds)), style: UITableViewStyle.Grouped)
? ? ? ? ? ? tableView.dataSource = self
? ? ? ? ? ? tableView.delegate = self
? ? ? ? ? ? return tableView
? ? ? ? ? ? }()
? ? ? ? self.view.addSubview(self.tableView!)
? ? }
? ? // MARK:UITableViewDataSource && UITableViewDelegate
? ? func numberOfSectionsInTableView(tableView: UITableView) -> Int {
? ? ? ? return (self.answers!.count)
? ? }
? ? func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
? ? ? ? let key = "\(section)"
? ? ? ? let answers = self.answers![key]
? ? ? ? return answers!.count
? ? }
? ? func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
? ? ? ? var cell: CustomTableViewCell? = tableView.dequeueReusableCellWithIdentifier("cell") as? CustomTableViewCell
? ? ? ? if cell == nil {
? ? ? ? ? ? cell = CustomTableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
? ? ? ? }
? ? ? ? cell?.indexPath = indexPath
? ? ? ? let key = "\(indexPath.section)"
? ? ? ? let answers = self.answers![key]
? ? ? ? self.selectedIndexPath?.row == indexPath.row ? cell?.setChecked(true) : cell?.setChecked(false)
? ? ? ? cell!.getIndexWithClosure { (indexPath) -> Void in
? ? ? ? ? ? self.selectedIndexPath = indexPath
? ? ? ? ? ? print("您選擇的答案是:\(answers![indexPath.row])")
? ? ? ? ? ? tableView.reloadSections(NSIndexSet(index: self.selectedIndexPath!.section), withRowAnimation: UITableViewRowAnimation.Automatic)
? ? ? ? }
? ? ? ? cell!.displayLab?.text = answers![indexPath.row]
? ? ? ? cell!.selectionStyle = UITableViewCellSelectionStyle.None
? ? ? ? return cell!
? ? }
? ? func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
? ? ? ? return 40
? ? }
? ? func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
? ? ? ? return self.questions![section]
? ? }
}CustomTableViewCell.swift文件
import UIKit
typealias IndexPathClosure = (indexPath: NSIndexPath) ->Void
class CustomTableViewCell: UITableViewCell {
? ? var choiceBtn: UIButton?
? ? var displayLab: UILabel?
? ? var indexPath: NSIndexPath?
? ? var indexPathClosure: IndexPathClosure?
? ? override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
? ? ? ? super.init(style: style, reuseIdentifier: reuseIdentifier)
? ? ? ? self.initializeUserInterface()
? ? }
? ? required init?(coder aDecoder: NSCoder) {
? ? ? ? fatalError("init(coder:) has not been implemented")
? ? }
? ? // MARK:Initialize methods
? ? func initializeUserInterface() {
? ? ? ? self.choiceBtn = {
? ? ? ? ? ? let choiceBtn = UIButton(type: UIButtonType.Custom)
? ? ? ? ? ? choiceBtn.bounds = CGRectMake(0, 0, 30, 30)
? ? ? ? ? ? choiceBtn.center = CGPointMake(20, 22)
? ? ? ? ? ? choiceBtn.setBackgroundImage(UIImage(named: "iconfont-select"), forState: UIControlState.Normal)
? ? ? ? ? ? choiceBtn.setBackgroundImage(UIImage(named: "iconfont-selected"), forState: UIControlState.Selected)
? ? ? ? ? ? choiceBtn.addTarget(self, action: Selector("respondsToButton:"), forControlEvents: UIControlEvents.TouchUpInside)
? ? ? ? ? ? return choiceBtn
? ? ? ? ? ? }()
? ? ? ? self.contentView.addSubview(self.choiceBtn!)
? ? ? ? self.displayLab = {
? ? ? ? ? ? let displayLab = UILabel()
? ? ? ? ? ? displayLab.bounds = CGRectMake(0, 0, 100, 30)
? ? ? ? ? ? displayLab.center = CGPointMake(CGRectGetMaxX(self.choiceBtn!.frame) + 60, CGRectGetMidY(self.choiceBtn!.frame))
? ? ? ? ? ? displayLab.textAlignment = NSTextAlignment.Left
? ? ? ? ? ? return displayLab
? ? ? ? ? ? }()
? ? ? ? self.contentView.addSubview(self.displayLab!)
? ? }
? ? // MARK:Events
? ? func respondsToButton(sender: UIButton) {
? ? ? ? sender.selected = true
? ? ? ? if self.indexPathClosure != nil {
? ? ? ? ? ? self.indexPathClosure!(indexPath: self.indexPath!)
? ? ? ? }
? ? }
? ? // MARK:Private
? ? func setChecked(checked: Bool) {
? ? ? ? self.choiceBtn?.selected = checked
? ? }
? ? func getIndexWithClosure(closure: IndexPathClosure?) {
? ? ? ? self.indexPathClosure = closure
? ? }
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Swift4.1轉(zhuǎn)場動畫實(shí)現(xiàn)側(cè)滑抽屜效果
這篇文章主要為大家詳細(xì)介紹了Swift4.1轉(zhuǎn)場動畫實(shí)現(xiàn)側(cè)滑抽屜效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-06-06
iOS開發(fā)中Swift 指紋驗證功能模塊實(shí)例代碼
本文給大家分享ios調(diào)用touchid代碼塊,非常不錯,具有參考借鑒價值,需要的朋友參考下把2017-03-03
Swift中動態(tài)調(diào)用實(shí)例方法介紹
這篇文章主要介紹了Swift中動態(tài)調(diào)用實(shí)例方法介紹,在Swift中有一類很有意思的寫法,可以讓我們不直接使用實(shí)例來調(diào)用這個實(shí)例上的方法,而是通過類型取出這個類型的某個實(shí)例方法的簽名,然后再通過傳遞實(shí)例來拿到實(shí)際需要調(diào)用的方法,需要的朋友可以參考下2015-01-01

