iOS中的UITableView中的下拉列表

在这里输入图像说明

如何在iOS中创build这种types的tableview?

在这里,如果我们点击第一行“帐户”,然后自动滚动与图像中显示更多的行。 如果我们再次点击帐户,那么这个视图将被隐藏。

12 Solutions collect form web for “iOS中的UITableView中的下拉列表”

你可以很容易地设置一个单元格,看起来像一个头,并设置tableView: didSelectRowAtIndexPath来手动扩大或收缩它的部分。 如果我将存储一个布尔值数组,对应每个部分的“扩展”值。 然后,您可以在每个自定义标题行上的tableView:didSelectRowAtIndexPath切换此值,然后重新加载该特定部分。

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0) { ///it's the first row of any section so it would be your custom section header ///put in your code to toggle your boolean value here mybooleans[indexPath.section] = !mybooleans[indexPath.section]; ///reload this section [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade]; } } 

然后,您将设置您的号码numberOfRowsInSection以检查mybooleans值,并返回1如果该部分未展开,或1 +该部分中的项目数,如果它展开。

 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (mybooleans[section]) { ///we want the number of people plus the header cell return [self numberOfPeopleInGroup:section] + 1; } else { ///we just want the header cell return 1; } } 

您还必须更新您的cellForRowAtIndexPath以返回任何section第一行的自定义标题单元格。

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section是更好的方式来提供您自己的“自定义标题”,因为这正是它的目的。

有关更多详细信息,请参阅此答案或此PKCollapsingTableViewSections 。

此外,您可以使用setIndentationLevel来获得这种types的tableviews。 请参考这个DemoCode这个例子。 我认为这是下拉式tableview的最佳解决scheme。

如果你想做一个简单的头和单元格下拉,那么请参考STCollapseTableView 。

希望,这是你要找的。 任何关心回到我身边。 🙂

通过表格视图单元格实现这个更简单也是最自然的方法。 没有扩展单元格视图,没有节标题,单纯的单元格(毕竟我们在表格视图中)。

devise如下:

  • 使用MVVM方法创build一个CollapsableViewModel类,该类包含configuration单元格所需的信息:label,image
  • 除了上面的那个外,还有两个额外的字段: children ,这是一个isCollapsed对象的数组, isCollapsed ,它保存下拉的状态
  • 视图控制器持有对CollapsableViewModel层次结构的引用,以及包含将在屏幕上呈现的视图模型( displayedRows属性)的平面列表,
  • 每当单击一个单元格时,检查它是否有子级,并通过insertRowsAtIndexPaths()deleteRowsAtIndexPaths()函数在displayedRows行和表视图中添加或删除行。

Swift代码如下(请注意,代码仅使用视图模型的label属性,以保持其干净):

 import UIKit class CollapsableViewModel { let label: String let image: UIImage? let children: [CollapsableViewModel] var isCollapsed: Bool init(label: String, image: UIImage? = nil, children: [CollapsableViewModel] = [], isCollapsed: Bool = true) { self.label = label self.image = image self.children = children self.isCollapsed = isCollapsed } } class CollapsableTableViewController: UITableViewController { let data = [ CollapsableViewModel(label: "Account", image: nil, children: [ CollapsableViewModel(label: "Profile"), CollapsableViewModel(label: "Activate account"), CollapsableViewModel(label: "Change password")]), CollapsableViewModel(label: "Group"), CollapsableViewModel(label: "Events", image: nil, children: [ CollapsableViewModel(label: "Nearby"), CollapsableViewModel(label: "Global"), ]), CollapsableViewModel(label: "Deals"), ] var displayedRows: [CollapsableViewModel] = [] override func viewDidLoad() { super.viewDidLoad() displayedRows = data } override func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return displayedRows.count } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier") ?? UITableViewCell() let viewModel = displayedRows[indexPath.row] cell.textLabel!.text = viewModel.label return cell } override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { tableView.deselectRowAtIndexPath(indexPath, animated: false) let viewModel = displayedRows[indexPath.row] if viewModel.children.count > 0 { let range = indexPath.row+1...indexPath.row+viewModel.children.count let indexPaths = range.map{return NSIndexPath(forRow: $0, inSection: indexPath.section)} tableView.beginUpdates() if viewModel.isCollapsed { displayedRows.insertContentsOf(viewModel.children, at: indexPath.row+1) tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic) } else { displayedRows.removeRange(range) tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic) } tableView.endUpdates() } viewModel.isCollapsed = !viewModel.isCollapsed } } 

Objective-C对应语言很容易翻译,我加了Swift版本,因为它更短,更易读。

通过几个小小的改动,代码可以用来生成多个级别的下拉列表。

编辑

人们问我关于分隔符的问题,可以通过添加一个自定义的类CollapsibleTableViewCell来实现,这个自定义的类使用视图模型进行configuration(最后,将单元configuration逻辑从控制器移到它所属的单元格)。 对于分隔符逻辑的信用只有一些单元格去回答这个 SO问题的人。

首先,更新模型,添加一个needsSeparator属性,告诉表视图单元格是否呈现分隔符:

 class CollapsibleViewModel { let label: String let image: UIImage? let children: [CollapsibleViewModel] var isCollapsed: Bool var needsSeparator: Bool = true init(label: String, image: UIImage? = nil, children: [CollapsibleViewModel] = [], isCollapsed: Bool = true) { self.label = label self.image = image self.children = children self.isCollapsed = isCollapsed for child in self.children { child.needsSeparator = false } self.children.last?.needsSeparator = true } } 

然后,添加单元类:

 class CollapsibleTableViewCell: UITableViewCell { let separator = UIView(frame: CGRectZero) func configure(viewModel: CollapsibleViewModel) { self.textLabel?.text = viewModel.label if(viewModel.needsSeparator) { separator.backgroundColor = UIColor.grayColor() contentView.addSubview(separator) } else { separator.removeFromSuperview() } } override func layoutSubviews() { super.layoutSubviews() let separatorHeight = 1 / UIScreen.mainScreen().scale separator.frame = CGRectMake(separatorInset.left, contentView.bounds.height - separatorHeight, contentView.bounds.width-separatorInset.left-separatorInset.right, separatorHeight) } } 

然后需要修改cellForRowAtIndexPath来返回这种types的单元格:

 override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = (tableView.dequeueReusableCellWithIdentifier("CollapsibleTableViewCell") as? CollapsibleTableViewCell) ?? CollapsibleTableViewCell(style: .Default, reuseIdentifier: "CollapsibleTableViewCell") cell.configure(displayedRows[indexPath.row]) return cell } 

最后一步,从xib或代码( tableView.separatorStyle = .None )中删除默认的表格视图单元格分隔符。

这是一个基于MVC的解决scheme。

为您的部分创build一个Model类ClsMenuGroup

 class ClsMenuGroup: NSObject { // We can also add Menu group's name and other details here. var isSelected:Bool = false var arrMenus:[ClsMenu]! } 

为您的行创build一个Model类ClsMenu

 class ClsMenu: NSObject { var strMenuTitle:String! var strImageNameSuffix:String! var objSelector:Selector! // This is the selector method which will be called when this menu is selected. var isSelected:Bool = false init(pstrTitle:String, pstrImageName:String, pactionMehod:Selector) { strMenuTitle = pstrTitle strImageNameSuffix = pstrImageName objSelector = pactionMehod } } 

在您的ViewController中创build组数组

  class YourViewController: UIViewController, UITableViewDelegate { @IBOutlet var tblMenu: UITableView! var objTableDataSource:HDTableDataSource! var arrMenuGroups:[AnyObject]! // MARK: - View Lifecycle override func viewDidLoad() { super.viewDidLoad() if arrMenuGroups == nil { arrMenuGroups = Array() } let objMenuGroup = ClsMenuGroup() objMenuGroup.arrMenus = Array() var objMenu = ClsMenu(pstrTitle: "Manu1", pstrImageName: "Manu1.png", pactionMehod: "menuAction1") objMenuGroup.arrMenus.append(objMenu) objMenu = ClsMenu(pstrTitle: "Menu2", pstrImageName: "Menu2.png", pactionMehod: "menuAction2") objMenuGroup.arrMenus.append(objMenu) arrMenuGroups.append(objMenuGroup) configureTable() } func configureTable(){ objTableDataSource = HDTableDataSource(items: nil, cellIdentifier: "SideMenuCell", configureCellBlock: { (cell, item, indexPath) -> Void in let objTmpGroup = self.arrMenuGroups[indexPath.section] as! ClsMenuGroup let objTmpMenu = objTmpGroup.arrMenus[indexPath.row] let objCell:YourCell = cell as! YourCell objCell.configureCell(objTmpMenu) // This method sets the IBOutlets of cell in YourCell.m file. }) objTableDataSource.sectionItemBlock = {(objSection:AnyObject!) -> [AnyObject]! in let objMenuGroup = objSection as! ClsMenuGroup return (objMenuGroup.isSelected == true) ? objMenuGroup.arrMenus : 0 } objTableDataSource.arrSections = self.arrMenuGroups tblMenu.dataSource = objTableDataSource tblMenu.reloadData() } // MARK: - Tableview Delegate func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { let objTmpGroup = self.arrMenuGroups[indexPath.section] as! ClsMenuGroup let objTmpMenu = objTmpGroup.arrMenus[indexPath.row] if objTmpMenu.objSelector != nil && self.respondsToSelector(objTmpMenu.objSelector) == true { self.performSelector(objTmpMenu.objSelector) // Call the method for the selected menu. } tableView.reloadData() } func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let arrViews:[AnyObject] = NSBundle.mainBundle().loadNibNamed("YourCustomSectionView", owner: self, options: nil) let objHeaderView = arrViews[0] as! UIView objHeaderView.sectionToggleBlock = {(objSection:AnyObject!) -> Void in let objMenuGroup = objSection as! ClsMenuGroup objMenuGroup.isSelected = !objMenuGroup.isSelected tableView.reloadData() } return objHeaderView } // MARK: - Menu methods func menuAction1(){ } func menuAction2(){ } } 

我已经使用HDTableDataSource来代替Tableview的数据源方法。 你可以从GithubfindHDTableDataSource的例子。

以上代码的优点是

  1. 您随时可以更改任何菜单或部分的顺序或者交换菜单和部分,而无需更改其他function。
  2. 你不需要在你的tableview的委托方法中添加其他的长代码
  3. 您可以分别为菜单项指定图标,标题或其他属性,如添加徽章数量,更改所选菜单的颜色等。
  4. 您也可以通过对现有代码应用较小的更改来使用多个单元格或部分

通常我通过设置行高来实现。 例如,您有两个菜单项和下拉列表:

  • 菜单1
    • 项目1.1
    • 项目1.2
    • 项目1.3
  • 菜单2
    • 项目2.1
    • 项目2.2

所以你必须创build一个2视图的表格视图。 第一部分包含4行(菜单1及其项目),秒部分包含3行(菜单2及其项目)。

您只能在第一行中设置高度。 如果用户单击第一行,则通过设置高度来扩展此部分行并重新加载此部分。

这样做的简单方法是使用UITableView节头作为cell->,set行数为0,section.count为collapse和expand状态。

  • 这是TableViewSection头,isExpand – > section.count否则返回0。

    – 正常细胞

    – 正常细胞

    – 正常细胞

  • 这是TableViewSection头,isExpand – > section.count否则返回0。

    – 正常细胞

    – 正常细胞

    – 正常细胞

 @interface TestTableViewController () { BOOL showMenu; } @implementation TestTableViewController - (void)viewDidLoad { [super viewDidLoad]; // Uncomment the following line to preserve selection between presentations. // self.clearsSelectionOnViewWillAppear = NO; // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem; [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"accountMenu"]; [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"accountSubMenu"]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 2; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if (section == 0) { // Account Menu return 1; } if (showMenu) { // Profile/Private Account/Change Password return 3; } // Hidden Account Menu return 0; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell; if (indexPath.section == 0) { cell = [tableView dequeueReusableCellWithIdentifier:@"accountMenu" forIndexPath:indexPath]; cell.textLabel.text = @"Account"; } else { cell = [tableView dequeueReusableCellWithIdentifier:@"accountSubMenu" forIndexPath:indexPath]; switch (indexPath.row) { case 0: cell.textLabel.text = @"Profile"; break; case 1: cell.textLabel.text = @"Private Account"; break; case 2: cell.textLabel.text = @"Change Password"; break; default: break; } } return cell; } -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.section == 0) { // Click on Account Menu showMenu = !showMenu; [tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationAutomatic]; } } 

希望它帮助:)

iOS框架中没有内置的控制树视图的视图–UIKit 。 正如其他用户所指出的,可能最简单的解决scheme(不使用任何外部库)是将一些自定义逻辑添加到UITableView的委托和数据源来模拟所需的行为。

幸运的是,有一些开源的库允许你像视图一样实现所需的树视图,而不用担心展开/折叠操作的细节。 有几个可用于iOS平台。 在大多数情况下,这些库只是包装UITableView并为您提供程序员友好的界面,让您专注于您的问题,而不是树视图的实现细节。

就我个人而言,我是RATreeView库的作者,它旨在最小化在iOS上创build树视图(如视图)所需的成本。 您可以查看示例项目(在Objective-c和Swift中可用)以检查此控件的工作方式和行为。 使用我的控件,创build所需的视图非常简单:

  1. DataObject结构将用于保存关于树视图节点的信息 – 它将负责保存关于单元格,它的图像(如果单元格有图像)和它的子单元(如果单元格有子单元)标题的信息。
 class DataObject { let name : String let imageURL : NSURL? private(set) var children : [DataObject] init(name : String, imageURL : NSURL?, children: [DataObject]) { self.name = name self.imageURL = imageURL self.children = children } convenience init(name : String) { self.init(name: name, imageURL: nil, children: [DataObject]()) } } 
  1. 我们将声明协议TreeTableViewCell并实现两个符合该协议的单元。 其中一个单元格将用于显示根项目,另一个将用于显示根项目的子项目。
 protocol TreeTableViewCell { func setup(withTitle title: String, imageURL: NSURL?, isExpanded: Bool) } class ChildTreeTableViewCell : UITableViewCell, TreeTableViewCell { func setup(withTitle title: String, imageURL: NSURL?, isExpanded: Bool) { //implementation goes here } } class RootTreeTableViewCell : UITableViewCell, TreeTableViewCell { func setup(withTitle title: String, imageURL: NSURL?, isExpanded: Bool) { //implementation goes here } } 
  1. 在视图控制器(MVC)或视图模型(MVVM)中,我们定义了负责备份树视图的数据结构。
 let profileDataObject = DataObject(name: "Profile") let privateAccountDataObject = DataObject(name: "Private Account") let changePasswordDataObject = DataObject(name: "Change Password") let accountDataObject = DataObject(name: "Account", imageURL: NSURL(string: "AccountImage"), children: [profileDataObject, privateAccountDataObject, changePasswordDataObject]) let groupDataObject = DataObject(name: "Group", imageURL: NSURL(string: "GroupImage"), children: []) let eventDataObject = DataObject(name: "Event", imageURL: NSURL(string: "EventImage"), children: []) let dealsDataObject = DataObject(name: "Deals", imageURL: NSURL(string: "DealsImage"), children: []) data = [accountDataObject, groupDataObject, eventDataObject, dealsDataObject] 
  1. 接下来,我们将需要从RATreeView的数据源实现几个方法。
 func treeView(treeView: RATreeView, numberOfChildrenOfItem item: AnyObject?) -> Int { if let item = item as? DataObject { return item.children.count //return number of children of specified item } else { return self.data.count //return number of top level items here } } func treeView(treeView: RATreeView, child index: Int, ofItem item: AnyObject?) -> AnyObject { if let item = item as? DataObject { return item.children[index] //we return child of specified item here (using provided `index` variable) } else { return data[index] as AnyObject //we return root item here (using provided `index` variable) } } func treeView(treeView: RATreeView, cellForItem item: AnyObject?) -> UITableViewCell { let cellIdentifier = item ? “TreeTableViewChildCell” : “TreeTableViewCellRootCell” let cell = treeView.dequeueReusableCellWithIdentifier(cellIdentifier) as! TreeTableViewCell //TreeTableViewCell is a protocol which is implemented by two kinds of //cells - the one responsible for root items in the tree view and another //one responsible for children. As we use protocol we don't care //which one is truly being used here. Both of them can be //configured using data from `DataItem` object. let item = item as! DataObject let isExpanded = treeView.isCellForItemExpanded(item) //this variable can be used to adjust look of the cell by determining whether cell is expanded or not cell.setup(withTitle: item.name, imageURL: item.imageURL, expanded: isExpanded) return cell } 

请注意,使用我的库,您不必关心展开和折叠单元格 – 它由RATreeView处理。 您只负责用于configuration单元格的数据 – 其余部分由控制器自己处理。

如果你不喜欢使用任何外部库,那么你可以做2个自定义单元格。 一个在展开之前显示,另一个在展开之后显示(具有不同的标识符)。 当你点击单元格时,检查单元格是否展开。 如果不是,请使用扩展的小区标识符,否则使用非扩展的小区标识符

这是制作扩展表格视图单元格的最好和最简单的方法。

您可以将账户视为一个单元,随即展开以显示三个button(“configuration文件”,“激活账户”,“更改密码”),但是会产生一个问题:点击三个button中的每一个将被视为“用户select帐户单元格“和触发器-tableView:didSelectRowAtIndexPath:与单元格的结果展开/折叠。

或者,您可以使每个隐藏的选项(“configuration文件”,“激活帐户”,“更改密码”)单独的表格视图单元格。 但是我不知道你怎样才能使三个单元格整体展开和收缩(而不是从零高度到完全展开)。

所以,最好的解决办法是:

  1. 偶数单元格(索引:0,2,4 …)完成“菜单标题”和“切换菜单打开/closures”(朝向下面描述的关联奇数单元格)的angular色。
  2. 插入(初始折叠的)“菜单体”单元格,每个单元格的每个选项(例如“Profile”,“Activate Account”,“Change Password”)垂直布置在奇数索引(1,3,5)中。 ..)。 使用target-action来响应用户select每个选项/button。
  3. 实现表视图的委托方法,以便只有偶数单元格(菜单标题)是可选的,并实现select逻辑来展开/折叠相应的奇数单元格(inside -tableView:didSelectRowAtIndexPath :)。 例如,select索引为0(“帐户”)的单元格会导致索引1处的单元格展开/折叠(带有“configuration文件”,“激活帐户”,“更改密码”选项的菜单)。

这不是UITableView最优雅的用法,但会完成工作。

你需要一个Collapsable TableView。 为了达到这个目的,在你的TableView中,你必须跟踪哪些部分被折叠(收缩)以及哪些被展开。 为此,您需要维护一组展开的部分索引,或者一个布尔数组,其中每个索引的值指示相应部分是否展开。 检查特定索引处的值,同时将高度分配给某一行。 检查此链接以获取更多帮助。

你可以在这里了解部分TableViews。

Github上还有第三方库,可以帮你省下麻烦。 看看CollapsableTableView或CollapsableTable-Swift

根据@sticker的回答,你可以绑定运行时

objc_setAssociatedObject

为部分索引,并使用他的逻辑。 当在标题视图上使用tapgesture的时候,你可以得到section index

 objc_getAssociatedObject. UITapGestureRecognizer *singleTapRecogniser = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(gestureHandler:)] autorelease]; [singleTapRecogniser setDelegate:self]; singleTapRecogniser.numberOfTouchesRequired = 1; singleTapRecogniser.numberOfTapsRequired = 1; [sectionHeaderView addGestureRecognizer:singleTapRecogniser]; 

如果你想要任何第三方库,那么你可以试试这个解决scheme。

我喜欢@Cristik解决scheme,前一段时间我有同样的问题,我的解决scheme遵循类似的原则; 所以这是我根据我的要求提出的:

  1. 为了使它更通用,表的项目不应该inheritance扩展function专用的类,而应该有一个协议来定义必要的属性

  2. 我们可以扩大的层次不应该有限制。 所以表格可以有选项,子选项,子选项等

  3. 表视图应该显示或隐藏单元格使用任何通常的animation(没有reloadData

  4. 扩展动作不一定附加到用户select单元格,单元格可以有一个UISwitch例如

实现的简化版本( https://github.com/JuanjoArreola/ExpandableCells )如下所示:

首先协议:

 protocol CellDescriptor: class { var count: Int { get } var identifier: String! { get } } 

一个不可展开的单元总是有1个计数:

 extension CellDescriptor { var count: Int { return 1 } } 

然后可扩展单元协议:

 protocol ExpandableCellDescriptor: CellDescriptor { var active: Bool { get set } var children: [CellDescriptor] { get set } subscript(index: Int) -> CellDescriptor? { get } func indexOf(cellDescriptor: CellDescriptor) -> Int? } 

关于swift的一个很酷的事情是我们可以在协议扩展中编写一些实现,并且所有符合的类都可以使用默认实现,所以我们可以编写count subscriptindexOf实现以及另外一些其他有用的函数,如下所示:

 extension ExpandableCellDescriptor { var count: Int { var total = 1 if active { children.forEach({ total += $0.count }) } return total } var countIfActive: Int { ... } subscript(index: Int) -> CellDescriptor? { ... } func indexOf(cellDescriptor: CellDescriptor) -> Int? { ... } func append(cellDescriptor: CellDescriptor) { children.append(cellDescriptor) } } 

完整的实现在文件CellDescriptor.swift中

此外,在同一个文件中,有一个名为CellDescriptionArray的类实现了ExpandableCellDescriptor ,并且不会显示单元格

现在,任何类都可以符合以前的协议,而不必从特定的类inheritance,例如github中的代码,我创build了几个类: OptionExpandableOption ,这是ExpandableOption样子:

 class ExpandableOption: ExpandableCellDescriptor { var delegate: ExpandableCellDelegate? var identifier: String! var active: Bool = false { didSet { delegate?.expandableCell(self, didChangeActive: active) } } var children: [CellDescriptor] = [] var title: String? } 

这是UITableViewCell的一个子类:

 class SwitchTableViewCell: UITableViewCell, CellDescrptionConfigurable { @IBOutlet weak var titleLabel: UILabel! @IBOutlet weak var switchControl: UISwitch! var cellDescription: CellDescriptor! { didSet { if let option = cellDescription as? ExpandableOption { titleLabel.text = option.title switchControl.on = option.active } } } @IBAction func activeChanged(sender: UISwitch) { let expandableCellDescriptor = cellDescription as! ExpandableCellDescriptor expandableCellDescriptor.active = sender.on } } 

请注意,您可以configuration单元格,并按您喜欢的方式进行分类,您可以添加图像,标签,开关等。 没有限制,也没有改变所需的协议。

最后在TableViewController中我们创build了选项树:

 var options = CellDescriptionArray() override func viewDidLoad() { super.viewDidLoad() let account = ExpandableOption(identifier: "ExpandableCell", title: "Account") let profile = Option(identifier: "SimpleCell", title: "Profile") let isPublic = ExpandableOption(identifier: "SwitchCell", title: "Public") let caption = Option(identifier: "SimpleCell", title: "Anyone can see this account") isPublic.append(caption) account.append(profile) account.append(isPublic) options.append(account) let group = ExpandableOption(identifier: "ExpandableCell", title: "Group") group.append(Option(identifier: "SimpleCell", title: "Group Settings")) options.append(group) ... } 

其余的实现现在非常简单:

 func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return options.count } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let option = options[indexPath.row]! let cell = tableView.dequeueReusableCellWithIdentifier(option.identifier, forIndexPath: indexPath) (cell as! CellDescrptionConfigurable).cellDescription = option (option as? ExpandCellInformer)?.delegate = self return cell } func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { guard let option = options[indexPath.row] else { return } guard let expandableOption = option as? ExpandableOption else { return } if expandableOption.identifier == "ExpandableCell" { expandableOption.active = !expandableOption.active } } func expandableCell(expandableCell: ExpandableCellDescriptor, didChangeActive active: Bool) { guard let index = options.indexOf(expandableCell) else { return } var indexPaths = [NSIndexPath]() for row in 1..<expandableCell.countIfActive { indexPaths.append(NSIndexPath(forRow: index + row, inSection: 0)) } if active { tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Fade) } else { tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.Fade) } } 

它可能看起来像很多代码,但大部分只写了一次,大部分绘制表视图所需的信息都存在于CellDescriptor.swift文件中,单元configuration代码存在于UITableViewCell子类中, TableViewController本身的代码很less。

希望能帮助到你。