吕学英 發表於 2022-1-26 15:37:00

Swift实现表格视图单元格单选(2)

<p>本文实例为大家分享了Swift实现表格视图单元格单选的具体代码,供大家参考,具体内容如下</p>
<h2>效果</h2>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202201/2022126144952951.gif?2022026145010" /></p>
<h2>前言</h2>
<p>前段时间写了一篇博客: 表格视图单元格单选(一),实现起来并不复杂,简单易懂。在实际开发中,可能会涉及到更为复杂的操作,比如多个<code>section</code> 下的单选,如上面展示的效果,当我们有这样的需求的时候,该如何实现呢?因为,在上篇文章中我所用的控件都是单元格自带的<code>imageView</code>以及<code>textLabel</code>,本文我将主要分享自定义选择按钮以及在多个<code>section</code>下实现单选的方法。</p>
<h2>准备</h2>
<p><strong>界面搭建与数据显示</strong></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202201/2022126153211114.jpg?2022026153227" /></p>
<p>这样的界面相信对大家而言,并不难,这里我不再做详细的讲解,值得一提的是数据源的创建,每一组的头部标题,我用一个数组<code>questions</code>存储,类型为:<code>?</code>,由于每一组中,单元格内容不一致,因此建议用字典存储。如下所示:</p>
<div class="jb51code"><pre class="brush:cpp;">var questions: ?
var answers:   ]?</pre></div>
<p>如果我用字典来存储数据,那字典的键我应该如何赋值呢?其实很简单,我们只需将<code>section</code>的值作为<code>key</code> 就Ok了,这样做的好处在于,我可以根据用户点击的 <code>section</code>来处理对应的数据,我们知道,表格视图的<code>section</code> 从 0 开始,因此字典赋值可以像下面提供的代码一样赋值,但要注意,<code>answers</code>的值需与<code>questions</code>里面的问题一致,才能满足实际的需求。</p>
<div class="jb51code"><pre class="brush:cpp;">self.questions = ["您的性别是:",
                  "您意向工作地点是:",
                  "您是否参加公司内部培训:"]

self.answers = ["0":["男", "女"],
                "1":["成都", "上海", "北京", "深圳"],
                "2":["参加", "不参加","不确定"]]</pre></div>
<p>接下来需要做的事情就是自定义单元格(<code>UITableViewCell</code>)了,比较简单,直接上代码,代码中涉及到的图片素材可到阿里矢量图中下载:</p>
<div class="jb51code"><pre class="brush:cpp;">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) {

    }
}</pre></div>
<p>表格视图数据源与代理的实现,如下所示:</p>
<div class="jb51code"><pre class="brush:cpp;">// MARK:UITableViewDataSource &amp;&amp; UITableViewDelegate

func numberOfSectionsInTableView(tableView: UITableView) -&gt; Int {
    // 直接返回 answers 键值对个数即可,也可返回 questions 个数;
    return (self.answers!.count)
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {

    // 根据 section 获取对应的 key
    let key = "\(section)"
    // 根据 key 获取对应的数据(数组)
    let answers = self.answers!
    // 直接返回数据条数,就是需要的行数
    return answers!.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -&gt; 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!


    cell!.selectionStyle = UITableViewCellSelectionStyle.None

    return cell!
}

func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -&gt; CGFloat {
    return 40
}

func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -&gt; String? {
    return self.questions!
}</pre></div>
<h2>实现</h2>
<p>技术点:在这里我主要会用到闭包回调,在自定义的单元格中,用户点击按钮触发方法时,闭包函数会被调用,并将用户点击的单元格的<code>indexPath</code>进行传递,然后根据<code>indexPath</code>进行处理,具体的实现方式,下面会慢慢讲到,闭包类似于Objective-C中的Block,有兴趣的朋友可深入了解Swift中的闭包使用。</p>
<p>首先,我们需要在<code>CustomTableViewCell.swift</code>文件中,声明一个闭包类型:</p>
<div class="jb51code"><pre class="brush:cpp;">typealias IndexPathClosure = (indexPath: NSIndexPath) -&gt;Void</pre></div>
<p>其次,声明一个闭包属性:</p>
<div class="jb51code"><pre class="brush:cpp;">var indexPathClosure: IndexPathClosure?</pre></div>
<p>现在,要做的事情就是声明一个闭包函数了,闭包函数主要用于在<code>ViewController.swift</code>文件中调用并且将需要传递的数据传递到<code>ViewController.swift</code>文件中。</p>
<div class="jb51code"><pre class="brush:cpp;">func getIndexWithClosure(closure: IndexPathClosure?) {
        self.indexPathClosure = closure
    }</pre></div>
<p>闭包函数已经有了,那么何时调用闭包函数呢?当用户点击单元格的时候,闭包函数会被调用,因此,我们只需要到选择按钮触发方法中去处理逻辑就好了,在触发方法中,我们需要将单元格的indexPath属性传递出去,但是,<code>UITableViewCell</code>并无<code>indexPath</code>属性,那应该怎么办呢?我们可以为它创建一个<code>indexPath</code>属性,在配置表格视图协议方法<code>cellForRowAtIndexPath:</code>时,我们赋值单元格的<code>indexPath</code>属性就OK了。</p>
<div class="jb51code"><pre class="brush:cpp;">var indexPath: NSIndexPath?</pre></div>
<div class="jb51code"><pre class="brush:cpp;">func respondsToButton(sender: UIButton) {
    sender.selected = true
    if self.indexPathClosure != nil {
        self.indexPathClosure!(indexPath: self.indexPath!)
    }
}</pre></div>
<p>现在在<code>CustomTableViewCell.swift</code>文件里面的操作就差不多了,但是,还缺少一步,我还需要定制一个方法,用于设置按钮的状态:</p>
<div class="jb51code"><pre class="brush:cpp;">func setChecked(checked: Bool) {

    self.choiceBtn?.selected = checked

}</pre></div>
<p>到了这一步,我们要做的事情就是切换到<code>ViewController.swift</code>文件中,找到表格视图协议方法<code>cellForRowAtIndexPath:</code>,主要的逻辑就在这个方法中处理,首先我们需要做的事情就是赋值自定义单元格的<code>indexPath</code>属性:</p>
<div class="jb51code"><pre class="brush:cpp;">cell?.indexPath = indexPath</pre></div>
<p>其次,我需要在<code>ViewController.swift</code>文件中,声明一个<code>selectedIndexPath</code>属性用于记录用户当前选中的单元格位置:</p>
<div class="jb51code"><pre class="brush:cpp;">var selectedIndexPath: NSIndexPath?</pre></div>
<p>接下来我会去做一个操作,判断协议方法参数<code>indexPath.row</code>,是否与<code>selectedIndexPath.row</code>一致,如果一致,则设为选中,否则设为未选中,这里可用三目运算符:</p>
<div class="jb51code"><pre class="brush:cpp;">self.selectedIndexPath?.row == indexPath.row ? cell?.setChecked(true) : cell?.setChecked(false)</pre></div>
<p>这里大家可能会有疑问,那就是为什么只判断<code>row</code>呢?不用判断<code>section</code>吗?当然不用,因为在刷新表格视图的时候我并没有调用<code>reloadData</code>方法,而是指定刷新某一组(<code>section</code>)就可以了,如果全部刷新,则无法保留上一组用户选择的信息,这将不是我们所需要的。</p>
<p>接下来,将是最后一步,调用回调方法,该方法会在每一次用户点击单元格的时候调用,并且返回用户当前点击的单元格的<code>indexPath</code>,在这里,我们需要将返回的<code>indexPath</code>赋值给<code>selectedIndexPath</code>属性。并且刷新指定<code>section</code>就OK了,代码如下:</p>
<div class="jb51code"><pre class="brush:cpp;">cell!.getIndexWithClosure { (indexPath) -&gt; Void in

    self.selectedIndexPath = indexPath
    print("您选择的答案是:\(answers!)")

    tableView.reloadSections(NSIndexSet(index: self.selectedIndexPath!.section), withRowAnimation: UITableViewRowAnimation.Automatic)   
}</pre></div>
<h2>完整代码</h2>
<p>可能大家还比较模糊,这里我将贴上完整的代码供大家参考</p>
<p>ViewController.swift文件</p>
<div class="jb51code"><pre class="brush:cpp;">import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{

    var tableView: UITableView?
    var questions: ?
    var answers: ]?


    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 &amp;&amp; UITableViewDelegate

    func numberOfSectionsInTableView(tableView: UITableView) -&gt; Int {
        return (self.answers!.count)
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {

        let key = "\(section)"
        let answers = self.answers!
        return answers!.count
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -&gt; 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!

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

        cell!.getIndexWithClosure { (indexPath) -&gt; Void in

            self.selectedIndexPath = indexPath

            print("您选择的答案是:\(answers!)")

            tableView.reloadSections(NSIndexSet(index: self.selectedIndexPath!.section), withRowAnimation: UITableViewRowAnimation.Automatic)

        }

        cell!.displayLab?.text = answers!
        cell!.selectionStyle = UITableViewCellSelectionStyle.None

        return cell!
    }

    func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -&gt; CGFloat {
        return 40
    }

    func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -&gt; String? {
        return self.questions!
    }

}</pre></div>
<p>CustomTableViewCell.swift文件</p>
<div class="jb51code"><pre class="brush:cpp;">import UIKit

typealias IndexPathClosure = (indexPath: NSIndexPath) -&gt;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
    }
}
</pre></div>
<p>以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持琼殿技术社区。</p>
                           
                            <div class="art_xg">
                              <b>您可能感兴趣的文章:</b><ul><li>swift自定义表格控件(UITableView)</li><li>swift表格控件使用方法详解(UITableview)</li><li>Swift实现表格视图单元格单选(1)</li><li>Swift实现表格视图单元格多选</li><li>Swift使用表格组件实现单列表</li></ul>
                            </div>

                        </div>
                        <!--endmain-->
頁: [1]
查看完整版本: Swift实现表格视图单元格单选(2)