ios 在 Swift 中点击时展开单元格

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26217480/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 03:02:13  来源:igfitidea点击:

Expand cell when tapped in Swift

iosuitableviewswift

提问by user3746428

I have been trying to implement a feature in my app so that when a user taps a cell in my table view, the cell expands downwards to reveal notes. I have found plenty of examples of this in Objective-C but I am yet to find any for Swift.

我一直在尝试在我的应用程序中实现一个功能,以便当用户点击我的表格视图中的一个单元格时,该单元格向下扩展以显示笔记。我在 Objective-C 中找到了很多这样的例子,但我还没有找到任何适用于 Swift 的例子。

This example seems perfect: Accordion table cell - How to dynamically expand/contract uitableviewcell?

这个例子看起来很完美:Accordion table cell - How to dynamic expand/contract uitableviewcell?

I had an attempt at translating it to Swift:

我曾尝试将其翻译成 Swift:

var selectedRowIndex = NSIndexPath()
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    selectedRowIndex = indexPath
    tableView.beginUpdates()
    tableView.endUpdates()
}

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if selectedRowIndex == selectedRowIndex.row && indexPath.row == selectedRowIndex.row {
        return 100
    }
    return 70
}

However this just seems to crash the app.

然而,这似乎只是使应用程序崩溃。

Any ideas?

有任何想法吗?

Edit:

编辑:

Here is my cellForRowAtIndexPath code:

这是我的 cellForRowAtIndexPath 代码:

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    var cell:CustomTransactionTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomTransactionTableViewCell

    cell.selectionStyle = UITableViewCellSelectionStyle.None

    if tableView == self.searchDisplayController?.searchResultsTableView {
        cell.paymentNameLabel.text = (searchResults.objectAtIndex(indexPath.row)) as? String
        //println(searchResults.objectAtIndex(indexPath.row))
        var indexValue = names.indexOfObject(searchResults.objectAtIndex(indexPath.row))
        cell.costLabel.text = (values.objectAtIndex(indexValue)) as? String
        cell.dateLabel.text = (dates.objectAtIndex(indexValue)) as? String

        if images.objectAtIndex(indexValue) as NSObject == 0 {
            cell.paymentArrowImage.hidden = false
            cell.creditArrowImage.hidden = true
        } else if images.objectAtIndex(indexValue) as NSObject == 1 {
            cell.creditArrowImage.hidden = false
            cell.paymentArrowImage.hidden = true
        }
    } else {
        cell.paymentNameLabel.text = (names.objectAtIndex(indexPath.row)) as? String
        cell.costLabel.text = (values.objectAtIndex(indexPath.row)) as? String
        cell.dateLabel.text = (dates.objectAtIndex(indexPath.row)) as? String

        if images.objectAtIndex(indexPath.row) as NSObject == 0 {
            cell.paymentArrowImage.hidden = false
            cell.creditArrowImage.hidden = true
        } else if images.objectAtIndex(indexPath.row) as NSObject == 1 {
            cell.creditArrowImage.hidden = false
            cell.paymentArrowImage.hidden = true
        }
    }
    return cell
}

Here are the outlet settings:

以下是插座设置:

enter image description here

enter image description here

采纳答案by rdelmar

The first comparison in your if statement can never be true because you're comparing an indexPath to an integer. You should also initialize the selectedRowIndex variable with a row value that can't be in the table, like -1, so nothing will be expanded when the table first loads.

if 语句中的第一个比较永远不会为真,因为您将 indexPath 与整数进行比较。您还应该使用表中不能包含的行值(如 -1)初始化 selectedRowIndex 变量,以便在表首次加载时不会展开任何内容。

var selectedRowIndex: NSIndexPath = NSIndexPath(forRow: -1, inSection: 0)

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    if indexPath.row == selectedRowIndex.row {
        return 100
    }
    return 70
}

Swift 4.2 var selectedRowIndex: NSIndexPath = NSIndexPath(row: -1, section: 0)

斯威夫特 4.2 var selectedRowIndex: NSIndexPath = NSIndexPath(row: -1, section: 0)

回答by Lucas Cerro

It took me quite a lot of hours to get this to work. Below is how I solved it.

我花了很多时间才让它工作。下面是我解决它的方法。

PS: the problem with @rdelmar's code is that he assumes you only have one sectionin your table, so he's only comparing the indexPath.row. If you have more than one section (or if you want to already account for expanding the code later) you should compare the whole index, like so:

PS:@rdelmar 代码的问题在于他假设你section的表中只有一个,所以他只是比较indexPath.row. 如果您有多个部分(或者如果您想在以后考虑扩展代码),您应该比较整个索引,如下所示:

1) You need a variable to tell which row is selected. I see you already did that, but you'll need to return the variable to a consistent "nothing selected" state (for when the user closes all cells). I believe the best way to do this is via an optional:

1)您需要一个变量来判断选择了哪一行。我看到您已经这样做了,但是您需要将变量返回到一致的“未选择”状态(当用户关闭所有单元格时)。我相信最好的方法是通过一个可选的:

var selectedIndexPath: NSIndexPath? = nil

2) You need to identify when the user selects a cell. didSelectRowAtIndexPathis the obvious choice. You need to account for three possible outcomes:

2)您需要识别用户何时选择单元格。didSelectRowAtIndexPath是显而易见的选择。您需要考虑三种可能的结果:

  1. the user is tapping on a cell and another cell is expanded
  2. the user is tapping on a cell and no cell is expanded
  3. the user is tapping on a cell that is already expanded
  1. 用户点击一个单元格,另一个单元格被展开
  2. 用户正在点击一个单元格并且没有单元格被展开
  3. 用户正在点击已展开的单元格

For each case we check if the selectedIndexPath is equal to nil (no cell expanded), equal to the indexPath of the tapped row (same cell already expanded) or different from the indexPath (another cell is expanded). We adjust the selectedIndexPath accordingly. This variable will be used to check the right rowHeight for each row. You mentioned in comments that didSelectRowAtIndexPath"didn't seem to be called". Are you using a println() and checking the console to see if it was called? I included one in the code below.

对于每种情况,我们检查 selectedIndexPath 是否等于 nil(未展开单元格)、等于被点击行的 indexPath(已展开的同一单元格)或与 indexPath 不同(展开另一个单元格)。我们相应地调整 selectedIndexPath。此变量将用于检查每行的正确 rowHeight。您在评论中提到didSelectRowAtIndexPath“似乎没有被调用”。您是否正在使用 println() 并检查控制台以查看它是否被调用?我在下面的代码中包含了一个。

PS: this doesn't work using tableView.rowHeightbecause, apparently, rowHeight is checked only once by Swift before updating ALL rows in the tableView.

PS:这不起作用,tableView.rowHeight因为显然,在更新 tableView 中的所有行之前,Swift 只检查了一次 rowHeight。

Last but not least, I use reloadRowsAtIndexPathto reload only the needed rows. But, also, because I know it will redraw the table, relayout when necessary and even animate the changes. Note the [indexPath]is between brackets because this method asks for an Array of NSIndexPath:

最后但并非最不重要的是,我使用reloadRowsAtIndexPath仅重新加载所需的行。但是,也因为我知道它会重绘表格,必要时重新布局,甚至动画更改。注意[indexPath]是在括号之间,因为这个方法要求一个 NSIndexPath 数组:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        println("didSelectRowAtIndexPath was called")
        var cell = tableView.cellForRowAtIndexPath(indexPath) as! MyCustomTableViewCell
        switch selectedIndexPath {
        case nil:
            selectedIndexPath = indexPath
        default:
            if selectedIndexPath! == indexPath {
                selectedIndexPath = nil
            } else {
                selectedIndexPath = indexPath
            }
        }
        tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}

3) Third and final step, Swift needs to know when to pass each value to the cell height. We do a similar check here, with if/else. I know you can made the code much shorter, but I'm typing everything out so other people can understand it easily, too:

3) 第三步也是最后一步,Swift 需要知道何时将每个值传递给单元格高度。我们在这里使用 if/else 进行类似的检查。我知道你可以让代码更短,但我正在输入所有内容,以便其他人也可以轻松理解:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        let smallHeight: CGFloat = 70.0
        let expandedHeight: CGFloat = 100.0
        let ip = indexPath
        if selectedIndexPath != nil {
            if ip == selectedIndexPath! {
                return expandedHeight
            } else {
                return smallHeight
            }
        } else {
            return smallHeight
        }
    }

Now, some notes on your code which might be the cause of your problems, if the above doesn't solve it:

现在,对您的代码进行一些说明,这可能是导致您出现问题的原因,如果上述方法不能解决问题:

var cell:CustomTransactionTableViewCell = self.tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as CustomTransactionTableViewCell

I don't know if that's the problem, but selfshouldn't be necessary, since you're probably putting this code in your (Custom)TableViewController. Also, instead of specifying your variable type, you can trust Swift's inference if you correctly force-cast the cell from the dequeue. That force casting is the as!in the code below:

我不知道这是否是问题所在,但self应该没有必要,因为您可能将此代码放在 (Custom)TableViewController 中。此外,如果您正确地从出队强制转换单元格,您可以相信 Swift 的推理,而不是指定您的变量类型。强制转换是as!在下面的代码中:

var cell = tableView.dequeueReusableCellWithIdentifier("CellIdentifier" forIndexPath: indexPath) as! CustomTransactionTableViewCell

However, you ABSOLUTELY need to set that identifier. Go to your storyboard, select the tableView that has the cell you need, for the subclass of TableViewCell you need (probably CustomTransactionTableViewCell, in your case). Now select the cell in the TableView (check that you selected the right element. It's best to open the document outline via Editor > Show Document Outline). With the cell selected, go to the Attributes Inspector on the right and type in the Identifiername.

但是,您绝对需要设置该标识符。转到您的故事板,选择具有您需要的单元格的 tableView,对于您需要的 TableViewCell 子类(可能CustomTransactionTableViewCell是您的情况)。现在选择 TableView 中的单元格(检查您是否选择了正确的元素。最好通过 Editor > Show Document Outline 打开文档大纲)。选择单元格后,转到右侧的属性检查器并输入Identifier名称。

You can also try commenting out the cell.selectionStyle = UITableViewCellSelectionStyle.Noneto check if that's blocking the selection in any way (this way the cells will change color when tapped if they become selected).

您还可以尝试注释掉cell.selectionStyle = UITableViewCellSelectionStyle.None以检查是否以任何方式阻止了选择(这样,如果单元格被选中,则在点击时会改变颜色)。

Good Luck, mate.

祝你好运,伙计。

回答by theDC

I suggest solving this with modyfing height layout constraint

我建议通过修改高度布局约束来解决这个问题

class ExpandableCell: UITableViewCell {

@IBOutlet weak var img: UIImageView!


@IBOutlet weak var imgHeightConstraint: NSLayoutConstraint!


var isExpanded:Bool = false
    {
    didSet
    {
        if !isExpanded {
            self.imgHeightConstraint.constant = 0.0

        } else {
            self.imgHeightConstraint.constant = 128.0
        }
    }
}

}

Then, inside ViewController:

然后,在 ViewController 中:

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
    super.viewDidLoad()
    self.tableView.delegate = self
    self.tableView.dataSource = self
    self.tableView.estimatedRowHeight = 2.0
    self.tableView.rowHeight = UITableViewAutomaticDimension
    self.tableView.tableFooterView = UIView()
}


// TableView DataSource methods
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 3
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell:ExpandableCell = tableView.dequeueReusableCell(withIdentifier: "ExpandableCell") as! ExpandableCell
    cell.img.image = UIImage(named: indexPath.row.description)
    cell.isExpanded = false
    return cell
}

// TableView Delegate methods
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    guard let cell = tableView.cellForRow(at: indexPath) as? ExpandableCell
        else { return }

        UIView.animate(withDuration: 0.3, animations: {
            tableView.beginUpdates()
            cell.isExpanded = !cell.isExpanded
            tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.top, animated: true)
            tableView.endUpdates()

        })

    }




func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
    guard let cell = tableView.cellForRow(at: indexPath) as? ExpandableCell
        else { return }
    UIView.animate(withDuration: 0.3, animations: {
        tableView.beginUpdates()
        cell.isExpanded = false
        tableView.endUpdates()
    })
}
}

Full tutorial available here

完整教程在这里

回答by Justin Fischer

A different approach would be to push a new view controller within the navigation stack and use the transition for the expanding effect. The benefits would be SoC (separation of concerns). Example Swift 2.0 projects for both patterns.

一种不同的方法是在导航堆栈中推送一个新的视图控制器,并使用转换来扩展效果。好处是 SoC(关注点分离)。两种模式的示例 Swift 2.0 项目。

回答by Abhishek

After getting the index path in didSelectRowAtIndexPathjust reload the cell with following method reloadCellsAtIndexpathand in heightForRowAtIndexPathMethodcheck following condition

获取索引路径后,didSelectRowAtIndexPath只需使用以下方法重新加载单元格 reloadCellsAtIndexpathheightForRowAtIndexPathMethod检查以下条件

if selectedIndexPath != nil && selectedIndexPath == indexPath {    
       return yourExpandedCellHieght
}