Swift實現表格視圖單元格單選(2)

本文實例為大傢分享瞭Swift實現表格視圖單元格單選的具體代碼,供大傢參考,具體內容如下

效果

前言

前段時間寫瞭一篇博客: 表格視圖單元格單選(一),實現起來並不復雜,簡單易懂。在實際開發中,可能會涉及到更為復雜的操作,比如多個section 下的單選,如上面展示的效果,當我們有這樣的需求的時候,該如何實現呢?因為,在上篇文章中我所用的控件都是單元格自帶的imageView以及textLabel,本文我將主要分享自定義選擇按鈕以及在多個section下實現單選的方法。

準備

界面搭建與數據顯示

這樣的界面相信對大傢而言,並不難,這裡我不再做詳細的講解,值得一提的是數據源的創建,每一組的頭部標題,我用一個數組questions存儲,類型為:[String]?,由於每一組中,單元格內容不一致,因此建議用字典存儲。如下所示:

var questions: [String]?
var answers:   [String:[String]]?

如果我用字典來存儲數據,那字典的鍵我應該如何賦值呢?其實很簡單,我們隻需將section的值作為key 就Ok瞭,這樣做的好處在於,我可以根據用戶點擊的 section來處理對應的數據,我們知道,表格視圖的section 從 0 開始,因此字典賦值可以像下面提供的代碼一樣賦值,但要註意,answers的值需與questions裡面的問題一致,才能滿足實際的需求。

self.questions = ["您的性別是:",
                  "您意向工作地點是:",
                  "您是否參加公司內部培訓:"]

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) {

    }
}

表格視圖數據源與代理的實現,如下所示:

// MARK:UITableViewDataSource && UITableViewDelegate

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    // 直接返回 answers 鍵值對個數即可,也可返回 questions 個數;
    return (self.answers!.count)
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    // 根據 section 獲取對應的 key
    let key = "\(section)"
    // 根據 key 獲取對應的數據(數組)
    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")
    }

    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]
}

實現

技術點:在這裡我主要會用到閉包回調,在自定義的單元格中,用戶點擊按鈕觸發方法時,閉包函數會被調用,並將用戶點擊的單元格的indexPath進行傳遞,然後根據indexPath進行處理,具體的實現方式,下面會慢慢講到,閉包類似於Objective-C中的Block,有興趣的朋友可深入瞭解Swift中的閉包使用。

首先,我們需要在CustomTableViewCell.swift文件中,聲明一個閉包類型:

typealias IndexPathClosure = (indexPath: NSIndexPath) ->Void

其次,聲明一個閉包屬性:

var indexPathClosure: IndexPathClosure?

現在,要做的事情就是聲明一個閉包函數瞭,閉包函數主要用於在ViewController.swift文件中調用並且將需要傳遞的數據傳遞到ViewController.swift文件中。

func getIndexWithClosure(closure: IndexPathClosure?) {
        self.indexPathClosure = closure
    }

閉包函數已經有瞭,那麼何時調用閉包函數呢?當用戶點擊單元格的時候,閉包函數會被調用,因此,我們隻需要到選擇按鈕觸發方法中去處理邏輯就好瞭,在觸發方法中,我們需要將單元格的indexPath屬性傳遞出去,但是,UITableViewCell並無indexPath屬性,那應該怎麼辦呢?我們可以為它創建一個indexPath屬性,在配置表格視圖協議方法cellForRowAtIndexPath:時,我們賦值單元格的indexPath屬性就OK瞭。

var indexPath: NSIndexPath?
func respondsToButton(sender: UIButton) {
    sender.selected = true
    if self.indexPathClosure != nil {
        self.indexPathClosure!(indexPath: self.indexPath!)
    }
}

現在在CustomTableViewCell.swift文件裡面的操作就差不多瞭,但是,還缺少一步,我還需要定制一個方法,用於設置按鈕的狀態:

func setChecked(checked: Bool) {

    self.choiceBtn?.selected = checked

}

到瞭這一步,我們要做的事情就是切換到ViewController.swift文件中,找到表格視圖協議方法cellForRowAtIndexPath:,主要的邏輯就在這個方法中處理,首先我們需要做的事情就是賦值自定義單元格的indexPath屬性:

cell?.indexPath = indexPath

其次,我需要在ViewController.swift文件中,聲明一個selectedIndexPath屬性用於記錄用戶當前選中的單元格位置:

var selectedIndexPath: NSIndexPath?

接下來我會去做一個操作,判斷協議方法參數indexPath.row,是否與selectedIndexPath.row一致,如果一致,則設為選中,否則設為未選中,這裡可用三目運算符:

self.selectedIndexPath?.row == indexPath.row ? cell?.setChecked(true) : cell?.setChecked(false)

這裡大傢可能會有疑問,那就是為什麼隻判斷row呢?不用判斷section嗎?當然不用,因為在刷新表格視圖的時候我並沒有調用reloadData方法,而是指定刷新某一組(section)就可以瞭,如果全部刷新,則無法保留上一組用戶選擇的信息,這將不是我們所需要的。

接下來,將是最後一步,調用回調方法,該方法會在每一次用戶點擊單元格的時候調用,並且返回用戶當前點擊的單元格的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 = ["您的性別是:", "您意向工作地點是:", "您是否參加公司內部培訓:"]

        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
    }
}

以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。

推薦閱讀: