效果

前言

前段时间写了一篇博客: 表格视图单元格单选(一),实现起来并不复杂,简单易懂。在实际开发中,可能会涉及到更为复杂的操作,比如多个 section 下的单选,如上面展示的效果,当我们有这样的需求的时候,该如何实现呢?因为,在上篇文章中我所用的控件都是单元格自带的imageView以及textLabel,本文我将主要分享自定义选择按钮以及在多个section下实现单选的方法。

准备

界面搭建与数据显示

这样的界面相信对大家而言,并不难,这里我不再做详细的讲解,值得一提的是数据源的创建,每一组的头部标题,我用一个数组questions 存储,类型为:[String]?,由于每一组中,单元格内容不一致,因此建议用字典存储。如下所示:

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

如果我用字典来存储数据,那字典的键我应该如何赋值呢?其实很简单,我们只需将 section 的值作为 key 就Ok了,这样做的好处在于,我可以根据用户点击的 section 来处理对应的数据,我们知道,表格视图的 section0 开始,因此字典赋值可以像下面提供的代码一样赋值,但要注意,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,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,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,forControlEvents: UIControlEvents.TouchUpInside)
            return choiceBtn
            }()
        self.contentView.addSubview(self.choiceBtn!)

        self.displayLab = {
            let displayLab = UILabel()
            displayLab.bounds = CGRectMake(0,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
    }
}

Swift:表格视图单元格单选二的更多相关文章

  1. 移动HTML5前端框架—MUI的使用

    这篇文章主要介绍了移动HTML5前端框架—MUI的使用的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. html5教你做炫酷的碎片式图片切换 (canvas)

    本篇文章主要介绍了html5教你做炫酷的碎片式图片切换 (canvas) ,具有一定参考价值,有兴趣的可以了解一下

  3. HTML5 weui使用笔记

    这篇文章主要介绍了HTML5 weui使用笔记,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  4. HTML5 WebSocket实现点对点聊天的示例代码

    这篇文章主要介绍了HTML5 WebSocket实现点对点聊天的示例代码的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. Html5写一个简单的俄罗斯方块小游戏

    这篇文章主要介绍了基于Html5写一个简单的俄罗斯方块小游戏,本文通过图文并茂的形式给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友参考下吧

  6. ios – 在Swift的UIView中找到UILabel

    我正在尝试在我的UIViewControllers的超级视图中找到我的UILabels.这是我的代码:这是在Objective-C中推荐的方式,但是在Swift中我只得到UIViews和CALayer.我肯定在提供给这个方法的视图中有UILabel.我错过了什么?我的UIViewController中的调用:解决方法使用函数式编程概念可以更轻松地实现这一目标.

  7. ios – 在Swift中将输入字段字符串转换为Int

    所以我非常擅长制作APP广告Swift,我试图在文本字段中做一些非常简单的输入,取值,然后将它们用作Int进行某些计算.但是’vardistance’有些东西不正确它是导致错误的最后一行代码.它说致命错误:无法解开Optional.None解决方法在你的例子中,距离是一个Int?否则称为可选的Int..toInt()返回Int?因为从String到Int的转换可能失败.请参阅以下示例:

  8. 如何在iOS中检测文本(字符串)语言?

    例如,给定以下字符串:我想检测每个声明的字符串中使用的语言.让我们假设已实现函数的签名是:如果没有检测到语言,则返回可选字符串.因此,适当的结果将是:有一个简单的方法来实现它吗?

  9. ios – UITableView和Cell Reuse

    这是我的CustomCell类的init方法解决方法如果没有要显示的图像,则必须清除图像视图:

  10. xamarin – 崩溃在AccountStore.Create().保存(e.Account,“);

    在Xamarin.Forms示例TodoAwsAuth中https://developer.xamarin.com/guides/xamarin-forms/web-services/authentication/oauth/成功登录后,在aOnAuthenticationCompleted事件中,应用程序在尝试保存到Xamarin.Auth时崩溃错误说不能对钥匙串说期待着寻求帮助.解决方法看看你

随机推荐

  1. Swift UITextField,UITextView,UISegmentedControl,UISwitch

    下面我们通过一个demo来简单的实现下这些控件的功能.首先,我们拖将这几个控件拖到storyboard,并关联上相应的属性和动作.如图:关联上属性和动作后,看看实现的代码:

  2. swift UISlider,UIStepper

    我们用两个label来显示slider和stepper的值.再用张图片来显示改变stepper值的效果.首先,这三个控件需要全局变量声明如下然后,我们对所有的控件做个简单的布局:最后,当slider的值改变时,我们用一个label来显示值的变化,同样,用另一个label来显示stepper值的变化,并改变图片的大小:实现效果如下:

  3. preferredFontForTextStyle字体设置之更改

    即:

  4. Swift没有异常处理,遇到功能性错误怎么办?

    本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请发送邮件至dio@foxmail.com举报,一经查实,本站将立刻删除。

  5. 字典实战和UIKit初探

    ios中数组和字典的应用Applicationschedule类别子项类别名称优先级数据包contactsentertainment接触UIKit学习用Swift调用CocoaTouchimportUIKitletcolors=[]varbackView=UIView(frame:CGRectMake(0.0,0.0,320.0,CGFloat(colors.count*50)))backView

  6. swift语言IOS8开发战记21 Core Data2

    上一话中我们简单地介绍了一些coredata的基本知识,这一话我们通过编程来实现coredata的使用。还记得我们在coredata中定义的那个Model么,上面这段代码会加载这个Model。定义完方法之后,我们对coredata的准备都已经完成了。最后强调一点,coredata并不是数据库,它只是一个框架,协助我们进行数据库操作,它并不关心我们把数据存到哪里。

  7. swift语言IOS8开发战记22 Core Data3

    上一话我们定义了与coredata有关的变量和方法,做足了准备工作,这一话我们来试试能不能成功。首先打开上一话中生成的Info类,在其中引用头文件的地方添加一个@objc,不然后面会报错,我也不知道为什么。

  8. swift实战小程序1天气预报

    在有一定swift基础的情况下,让我们来做一些小程序练练手,今天来试试做一个简单地天气预报。然后在btnpressed方法中依旧增加loadWeather方法.在loadWeather方法中加上信息的显示语句:运行一下看看效果,如图:虽然显示出来了,但是我们的text是可编辑状态的,在storyboard中勾选Editable,再次运行:大功告成,而且现在每次单击按钮,就会重新请求天气情况,大家也来试试吧。

  9. 【iOS学习01】swift ? and !  的学习

    如果不初始化就会报错。

  10. swift语言IOS8开发战记23 Core Data4

    接着我们需要把我们的Rest类变成一个被coredata管理的类,点开Rest类,作如下修改:关键字@NSManaged的作用是与实体中对应的属性通信,BinaryData对应的类型是NSData,CoreData没有布尔属性,只能用0和1来区分。进行如下操作,输入类名:建立好之后因为我们之前写的代码有些地方并不适用于coredata,所以编译器会报错,现在来一一解决。

返回
顶部