I have revised the question completely after my latest findings.
My goal is to implement the following effect:
Please note that I am aware, that this can be achieved by inserting/deleting cells below the selected one, I already have a successful implementation using that method .
This time, I want to try to achieve this using Auto Layout constraints.
I have a sample project available for anyone to check , and also opened an issue . To summarize, here's what I've tried so far:
I have the following views as actors here:
I have set up Auto Layout constraints within my cell the following way (please note that this is strictly pseudo-language):
I have a custom UITableViewCell
subclass, with multiple outlets, but the most important here is an outlet for the height constraint mentioned previously: the idea here is to set its constant
to 0 by default, but when the cell is selected, set it to 44, so it becomes visible:
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
detailViewHeightConstraint.constant = selected ? detailViewDefaultHeight : 0
UIView.animateWithDuration(0.3) {
self.layoutIfNeeded()
}
}
I have the following result:
So the effect is working, but not exactly how I originally imagined. Instead of pushing the main view up, I want the cell's height to grow when the detail view is shown, and shrink back when it's hidden.
I have examined my layout hierarchy during runtime:
What I need is the following: the height of table view cell's content view should be equal to
How do I have to set my constraints to achieve that?
After a significant amount of research, I think I've found the solution with the help of this great article.
Here are the steps needed to make the cell resize:
Within the Main, and Detail Views, I have originally set the labels to be horizontally and vertically centered. This isn't enough for self sizing cells. The first thing I needed is to set up my layout using vertical spacing constraints instead of simple alignment:
Additionally you should set the Main Container's vertical compression resistance to 1000.
The detail view is a bit more tricky: Apart from creating the appropriate vertical constraints, you also have to play with their priorities to reach the desired effect:
The main idea is the following:
I had to modify my UITableViewCell
subclass to support these operations:
// `showDetails` is exposed to control, whether the cell should be expanded
var showsDetails = false {
didSet {
detailViewHeightConstraint.priority = showsDetails ? lowLayoutPriority : highLayoutPriority
}
}
override func awakeFromNib() {
super.awakeFromNib()
detailViewHeightConstraint.constant = 0
}
To trigger the behavior, we must override tableView(_:didSelectRowAtIndexPath:)
:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
switch expandedIndexPath {
case .Some(_) where expandedIndexPath == indexPath:
expandedIndexPath = nil
case .Some(let expandedIndex) where expandedIndex != indexPath:
expandedIndexPath = nil
self.tableView(tableView, didSelectRowAtIndexPath: indexPath)
default:
expandedIndexPath = indexPath
}
}
Notice that I've introduced expandedIndexPath
to keep track of our currently expanded index:
var expandedIndexPath: NSIndexPath? {
didSet {
switch expandedIndexPath {
case .Some(let index):
tableView.reloadRowsAtIndexPaths([index], withRowAnimation: UITableViewRowAnimation.Automatic)
case .None:
tableView.reloadRowsAtIndexPaths([oldValue!], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
}
Setting the property will result in the table view reloading the appropriate indexes, giving us a perfect opportunity to tell the cell, if it should expand:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ExpandableTableViewCell
cell.mainTitle = viewModel.mainTitleForRow(indexPath.row)
cell.detailTitle = viewModel.detailTitleForRow(indexPath.row)
switch expandedIndexPath {
case .Some(let expandedIndexPath) where expandedIndexPath == indexPath:
cell.showsDetails = true
default:
cell.showsDetails = false
}
return cell
}
The last step is to enable self-sizing in viewDidLoad()
:
override func viewDidLoad() {
super.viewDidLoad()
tableView.contentInset.top = statusbarHeight
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 125
}
Here is the result:
Cells now correctly size themselves. You may notice that the animation is still a bit weird, but fixing that does not fall into the scope of this question.
Conclusion: this was way harder than it should be. 😀 I really hope to see some improvements in the future.
This is in obj-c, but I'm sure you'll handle that:
Add in your viewDidLoad:
self.tableView.estimatedRowHeight = self.tableView.rowHeight; self.tableView.rowHeight = UITableViewAutomaticDimension;
This will enable self sizing cells for your tableView, and should work on iOS8+
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.