使用swift中的tableView(可扩展单元格)创建嵌套的折叠式菜单

时间:2016-05-12 10:39:54

标签: swift tableview accordion expandable-table

我有一个像下面的手风琴菜单的例子,我想做一个嵌套菜单(例如菜单中的菜单),它主要与可扩展的tableViews有关,但我需要在expandable tableView或任何其他解决方案中展开 这是来自互联网的代码,它执行单步手风琴,我需要嵌套的手风琴: PS:我的项目有点沉重,所以我不想添加其他库,也许只是一个类,非常感谢你提前

//  Created by ingdanni on 05/11/15.
//  Copyright (c) 2015 ManaguaIO. All rights reserved.
//

import UIKit

struct Section {
    let title: String
    let rows: [String]
    var selected: Bool
}

class ViewController: UIViewController {

    let CellIdentifier = "Cell"
    var sections = [Section]()

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Setup Sections
        sections.append(Section(title: "A", rows: ["1","2","3","4"], selected: false))
        sections.append(Section(title: "B", rows: ["5","6","7","8"], selected: false))
        sections.append(Section(title: "C", rows: ["9","10"], selected: false))

        // Set cell reuse identifier
        self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: CellIdentifier)

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return sections.count
    }


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

        if sections[section].selected
        {
            return sections[section].rows.count
        }
        return 0
    }

    func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return ""
    }

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

        return 50
    }

    func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        return 1
    }

    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        if sections[indexPath.section].selected {
            return 50
        }
        return 2
    }

    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

        let headerView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 40))

        headerView.backgroundColor = UIColor.lightGrayColor()
        headerView.tag = section

        let headerString = UILabel(frame: CGRect(x: 10, y: 10, width: tableView.frame.size.width-10, height: 30)) as UILabel

        headerString.text = sections[section].title
        headerView.addSubview(headerString)

        let headerTapped = UITapGestureRecognizer(target: self, action:"sectionHeaderTapped:")
        headerView.addGestureRecognizer(headerTapped)

        return headerView
    }

    func sectionHeaderTapped(recognizer: UITapGestureRecognizer) {

        let indexPath = NSIndexPath(forRow: 0, inSection:(recognizer.view?.tag as Int!)!)

        if indexPath.row == 0 {

            sections[indexPath.section].selected = !sections[indexPath.section].selected

            //reload specific section animated
            let range = NSMakeRange(indexPath.section, 1)

            let sectionToReload = NSIndexSet(indexesInRange: range)

            self.tableView.reloadSections(sectionToReload, withRowAnimation:UITableViewRowAnimation.Fade)
        }

    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{

        let cell = self.tableView.dequeueReusableCellWithIdentifier(CellIdentifier, forIndexPath: indexPath)
        cell.textLabel?.text = sections[indexPath.section].rows[indexPath.row]
        return cell
    }


}

like this

and schematically like this

1 个答案:

答案 0 :(得分:0)

实际上在经历了如何制作嵌套手风琴之后,我找到了答案,但后来又决定不将它用于来自服务器和JSON Web服务的动态数据(例如,当你有嵌套的树形菜单时,它具有动态结构来自因此我在一个页面中使用了每个类别并定义了三个级别并使用了segue,但是如果你需要创建这样的菜单,我发现制作NSObject的技巧就像在这里定义每个单元格的属性一样:

import UIKit

class AMPGenericObject: NSObject {

    var name:String?
    var parentName:String?
    var canBeExpanded = false // Bool to determine whether the cell can be expanded
    var isExpanded = false    // Bool to determine whether the cell is expanded
    var level:Int?            // Indendation level of tabelview
    var type:Int?
    var children:[AMPGenericObject] = []

    enum ObjectType:Int{
        case OBJECT_TYPE_REGION = 0
        case OBJECT_TYPE_LOCATION
        case OBJECT_TYPE_USERS
    }

}

这里是viewController:

import UIKit

class AMPTableViewController: UITableViewController {


var dataArray:[AMPGenericObject] = []
var indendationLevel:Int   = 0
var indendationWidth:CGFloat = 20.0

override func viewDidLoad() {
    super.viewDidLoad()

    for var i=0; i<=10; i++ {

    }

    for i in 0..<10 {

        let prod = AMPGenericObject()
        prod.name = "Region \(i)"
        prod.parentName = ""
        prod.isExpanded = false
        prod.level = 0;
        prod.type  = 0;
        // Randomly assign canBeExpanded status
        let rem = i % 2
        if(rem == 0)
        {
            prod.canBeExpanded  = true;
        }
        else
        {
            prod.canBeExpanded = false;
        }
        dataArray.append(prod)

    }
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

// MARK: - Table view data source

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return dataArray.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
    let obj = dataArray[indexPath.row]
    // All optionals are ensured to have values, so we can safely unwrap
    cell.textLabel!.text = obj.name!;
    cell.detailTextLabel!.text = obj.parentName!;
    cell.indentationLevel = obj.level!;
    cell.indentationWidth = indendationWidth;
    // Configure the cell...
    // Show disclosure only if the cell can expand
    if(obj.canBeExpanded)
    {
        cell.accessoryView = self.viewForDisclosureForState(obj.isExpanded)
    }
    else
    {
        //cell.accessoryType = UITableViewCellAccessoryNone;
        cell.accessoryView = nil;
    }

    return cell
}

func viewForDisclosureForState(isExpanded:Bool)->UIView{

    var imageName:String = ""
    if(isExpanded)
    {
        imageName = "ArrowD_blue";
    }
    else
    {
        imageName = "ArrowR_blue";
    }
    let view = UIView(frame: CGRectMake(0, 0, 40, 40))
    let imageView = UIImageView(image: UIImage(named: imageName))
    imageView.frame = CGRectMake(0, 6, 24, 24)
    view.addSubview(imageView)
    return view
}

func fetchChildrenforParent(parentProduct:AMPGenericObject){

    // If canBeExpanded then only we need to create child
    if(parentProduct.canBeExpanded)
    {
        // If Children are already added then no need to add again
        if(parentProduct.children.count > 0){
        return
        }
        // The children property of the parent will be filled with this objects
        // If the parent is of type region, then fetch the location.
        if (parentProduct.type == 0) {
            for i in 0..<10
            {
                let prod = AMPGenericObject()
                prod.name = "Location \(i)"
                prod.level  = parentProduct.level! + 1;
                prod.parentName = "Child \(i) of Level \(prod.level!)"
                // This is used for setting the indentation level so that it look like an accordion view
                prod.type = 1 //OBJECT_TYPE_LOCATION;
                prod.isExpanded = false;

                if(i % 2 == 0)
                {
                    prod.canBeExpanded = true
                }
                else
                {
                    prod.canBeExpanded = false
                }
                parentProduct.children.append(prod)
            }
        }
            // If tapping on Location, fetch the users
        else{

            for i in 0..<10
            {
                let prod = AMPGenericObject()
                prod.name = "User \(i)"
                prod.level  = parentProduct.level! + 1;
                prod.parentName = "Child \(i) of Level \(prod.level!)"
                // This is used for setting the indentation level so that it look like an accordion view
                prod.type = 1 //OBJECT_TYPE_LOCATION;
                prod.isExpanded = false;
                // Users need not expand
                prod.canBeExpanded = false
                parentProduct.children.append(prod)
            }
        }

    }
}

func collapseCellsFromIndexOf(prod:AMPGenericObject,indexPath:NSIndexPath,tableView:UITableView)->Void{

    // Find the number of childrens opened under the parent recursively as there can be expanded children also
    let collapseCol = self.numberOfCellsToBeCollapsed(prod)
    // Find the end index by adding the count to start index+1
    let end = indexPath.row + 1 + collapseCol
    // Find the range from the parent index and the length to be removed.
    let collapseRange =  Range(start: indexPath.row+1, end: end)
    // Remove all the objects in that range from the main array so that number of rows are maintained properly
    dataArray.removeRange(collapseRange)
    prod.isExpanded = false
    // Create index paths for the number of rows to be removed
    var indexPaths = [NSIndexPath]()
    for i in 0..<collapseRange.count {
        indexPaths.append(NSIndexPath.init(forRow: collapseRange.startIndex+i, inSection: 0))
    }
    // Animate and delete
    tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Left)

}

func expandCellsFromIndexOf(prod:AMPGenericObject,indexPath:NSIndexPath,tableView:UITableView)->Void{

    // Create dummy children
    self.fetchChildrenforParent(prod)


    // Expand only if children are available
    if(prod.children.count>0)
    {
        prod.isExpanded = true
        var i = 0;
        // Insert all the child to the main array just after the parent
        for prodData in prod.children {
            dataArray.insert(prodData, atIndex: indexPath.row+i+1)
            i++;
        }
        // Find the range for insertion
        let expandedRange = NSMakeRange(indexPath.row, i)

        var indexPaths = [NSIndexPath]()
        // Create index paths for the range
        for i in 0..<expandedRange.length {
            indexPaths.append(NSIndexPath.init(forRow: expandedRange.location+i+1, inSection: 0))
        }
        // Insert the rows
        tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Left)
    }
}

func numberOfCellsToBeCollapsed(prod:AMPGenericObject)->Int{

    var total = 0

    if(prod.isExpanded)
    {
        // Set the expanded status to no
        prod.isExpanded = false
        let child = prod.children
        total = child.count

        // traverse through all the children of the parent and get the count.
        for prodData in child{

            total += self.numberOfCellsToBeCollapsed(prodData)
        }
    }
    return total
}

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    let prod = dataArray[indexPath.row]
    let selectedCell = tableView.cellForRowAtIndexPath(indexPath)

    if(prod.canBeExpanded)
    {
        if(prod.isExpanded){
        self.collapseCellsFromIndexOf(prod, indexPath: indexPath, tableView: tableView)
        selectedCell?.accessoryView = self.viewForDisclosureForState(false)
        }
        else{
            self.expandCellsFromIndexOf(prod, indexPath: indexPath, tableView: tableView)
            selectedCell?.accessoryView = self.viewForDisclosureForState(true)
        }
    }

}

}

我特别感谢用户anoopm,我丢失了存储库的链接,任何人都有把它放在这里很好,感谢阅读本文的方式