如何使用begin-endUpdates()和多个自定义单元格展开/折叠UITableViewCells?

时间:2016-10-28 14:22:31

标签: ios swift uitableview reloaddata

背景:单个ViewController包含4个自定义原型单元格。第一个始终可见,并且有一个UITextField。 原型细胞2,3和4分别包含图像,标签和标签。但是,原型单元4基于一个数组,根据第一个单元格中的输入可以有一行或多行。

当在第一个单元格中输入某个String时,会检查是否存在一个Object,其中一个属性中包含该String。如果值不正确,则第一个单元格会更改高度和布局。如果值正确,则第一个单元格会更改高度,最重要的是,其他3个原型单元格将展开以显示与String输入对应的对象的详细信息。如果用户输入另一个不正确的字符串,则单元格会崩溃。

问题:动画显示其他3个单元格的展开/折叠。我在查找如何在beginUpdates()和endUpdates()之间定义numberOfRowsInSection()方法和代码块(我的代码中的第2步)时遇到了麻烦。即使没有arraycells实现,在insertRows()之后调用reloadRows()似乎也不起作用。

我尝试了什么:

  • reloadData()=正确显示数据,但我无法使用它,因为它无法提供必要的动画。
  • beginUpdates()和endUpdates()之间没有任何内容=给出以下错误:
      

    无效更新:第0节中的行数无效。更新后的现有部分中包含的行数(6)必须等于更新前该部分中包含的行数(1),或者减去从该部分插入或删除的行数(0插入,0删除)和加或减移入或移出该部分的行数(0移入,0移出)。'

我认为这与最后一个原型单元基于一个数组并且我没有重新加载单元的实际数据这一事实有关,只是视图。

  • 与insertRowsAtIndexPath,reloadRowsAtIndexPath,... =的其他组合给出与行数相关的类似错误。

非常感谢任何帮助!

简化代码:

class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, updateUITableView {

    var people: [Person] = [
        Person(name: "name", image: "imagename1", description: "description1", children: ["name1","name2","name3"]),
        Person(name: "name2", image: "imagename3", description: "description2", children: ["name4", "name5", "name6"])
    ]

    enum Flag {
        case start
        case match
        case noMatch
    }
    var flag = Flag.start

    var globalObject: Person?

    @IBOutlet var tableView: UITableView!

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        switch (flag) {
        case .start :
            return 1
        case .noMatch:
            return 1
        case .match:
            return 3 + (globalObject?.children.count)! //PROBLEM!
        }
    }

    //START - Input received from tableViewCell
    func checkName(senderCell: SearchInputTableViewCell, name: String) {
        // STEP 1: Check if person exists
        let person = checkPersonNameMatch(nameInput: name)
        globalObject = person //save globally for cellForRowsAtIndexPath

        // STEP 2: Show/hide cells
        toggleCellsVisibility(person: person)
    }

    //STEP 1:
    func checkPersonNameMatch(nameInput: String) -> Person? {
        guard let index = people.index(where: {$0.name == nameInput}) else {
            flag = .noMatch
            return nil
        }
        let personFound = people[index]
        flag = .match
        globalObject = personFound
        return personFound
    }

    //STEP 2 = PROBLEM!
        func toggleCellsVisibility(person: Person?) {
    if person != nil { //Cells appear
        UIView.animate(withDuration: 0.7, animations: {

            self.tableView.beginUpdates()

            let indexPath: IndexPath = IndexPath(row: 1, section: 0) //for Image cell
            let indexPath2: IndexPath = IndexPath(row: 2, section: 0) //for Description cell
            //let indexPath3: IndexPath = IndexPath[....] //for array in Children cell

            self.tableView.insertRows(at: [indexPath], with: .bottom)
            self.tableView.insertRows(at: [indexPath2], with: .bottom)

            self.tableView.reloadRows(at: [indexPath], with: .bottom)
            self.tableView.reloadRows(at: [indexPath2], with: .bottom)
            //... a for-loop to reload the array cells here?

            self.tableView.endUpdates()
        })
    } else { //Cells disappear
        UIView.animate(withDuration: 0.4, animations: {
            self.tableView.beginUpdates()
            //... same code as above?
            self.tableView.endUpdates()
        })
    }
}

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        switch indexPath.row {
        case 0:
            let cellIdentifier = "InputCell"
            let inputCell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! InputTableViewCell

            inputCell.delegate = self

            switch (flag) {
            case .start:
                self.tableView.rowHeight = 187
            case .match:
                self.tableView.rowHeight = 170
            case .noMatch:
                self.tableView.rowHeight = 200
            }

            return inputCell

        case 1:
            let cellIdentifier = "ImageCell"
            let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! ImageTableViewCell

            cell.image?.image = UIImage(named: (globalObject?.image)!)

            switch (flag) {
            case .start:
                self.tableView.rowHeight = 0
            case .match:
                self.tableView.rowHeight = 170
            case .noMatch:
                self.tableView.rowHeight = 0
            }

            return cell

        case 2:
            let cellIdentifier = "DescriptionCell"
            let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! DescriptionTableViewCell

            cell.description?.text = globalObject?.description

            switch (flag) {
            case .start:
                self.tableView.rowHeight = 0
            case .match:
                self.tableView.rowHeight = 54
            case .noMatch:
                self.tableView.rowHeight = 0
            }

            return cell

        default:
            let cellIdentifier = "ChildrenCell"
            let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! ChildrenTableViewCell

            cell.childName?.text = globalObject?.children[(indexPath as IndexPath).row - 3]

            switch (flag) {
            case .start:
                self.tableView.rowHeight = 0
            case .match:
                self.tableView.rowHeight = 44
            case .noMatch:
                self.tableView.rowHeight = 0
            }

            return cell
        }
    }
}

2 个答案:

答案 0 :(得分:0)

我的建议是:不要插入或删除这些行。只需将行保持在那里 - 高度为零(clipsToBounds设置为true)。因此,在您需要它们之前,它们对用户是不可见的。当您需要它们时,只需更改高度并拨打beginUpdatesendUpdates

答案 1 :(得分:0)

我建议您使用枚举更改数据布局,这样,您就不必担心行数,您可以在获得数据时进行计算。示例代码如下:

enum CellType {
    case textField
    case labelCell
    case array
}

在你的tableView中包含与

相关的内容
class TableViewController: UITableViewController {
    var order = [CellType]()

    func calculateRowOrder() {
        // You can also customize which cells you want here, or if they're dynamic
        order = [.textField, .labelCell]

        // Add as many .array as child cells
        for _ in globalObject?.children ?? [] {
            order.append(.array)
        }
    }
}

然后您可以更轻松地为数据源执行方法:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return order.count
}

细胞定制:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let row = indexPath.row

    let cellType = order[row]

    switch cellType {
    case .textField:
        <Customize textField>
    case .label:
        <Customize label>
    case .array:
        // You can figure out which child element you're customizing
        <Customize array>
    }
}

您可以随时更改数据,或者需要重新加载数据,重新计算订单并重新加载数据。祝你好运。