在Swift中点击时展开单元格

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

这个例子看起来很完美: 手风琴表格单元格 – 如何动态扩展/收缩uitableviewcell?

我试图将其翻译成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 } 

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

有任何想法吗?

编辑:

这是我的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 } 

以下是sockets设置:

在此处输入图像描述

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

 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 } 

我花了很多时间才开始工作。 以下是我如何解决它。

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

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

 var selectedIndexPath: NSIndexPath? = nil 

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

  1. 用户正在点击一个单元格,另一个单元格被扩展
  2. 用户正在点击一个单元格,没有单元格被扩展
  3. 用户正在点击已扩展的单元格

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

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

最后但并非最不重要的是,我使用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)第三步也是最后一步,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 } } 

现在,如果上述方法无法解决问题,请注意您的代码可能导致问题的原因:

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

我不知道这是不是问题,但是self不应该是必需的,因为你可能把这些代码放在你的(自定义)TableViewController中。 此外,如果您正确地强制从出队中强制转换单元格,您可以信任Swift的推理,而不是指定您的变量类型。 那种力量铸就是as! 在下面的代码中:

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

但是,您绝对需要设置该标识符。 转到你的故事板,选择你需要的CustomTransactionTableViewCell的tableView,你需要的TableViewCell的子类(在你的情况下可能是CustomTransactionTableViewCell )。 现在选择TableView中的单元格(检查是否选择了正确的元素。最好通过编辑器>显示文档大纲打开文档大纲)。 选择单元格后,转到右侧的“属性”检查器,然后键入Identifier名称。

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

祝你好运,交配。

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

我建议用modyfing高度布局约束来解决这个问题

 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 } } } } 

然后,在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() }) } } 

这里有完整的教程

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

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