Reputation: 208
Apple doc say : reloadRowsAtIndexPaths:withRowAnimation
:
:
Call this method if you want to alert the user that the value of a cell is changing. If, however, notifying the user is not important—that is, you just want to change the value that a cell is displaying—you can get the cell for a particular row and set its new value.
My problem scenario is that i want to update a label in a cell on button click and also update the layout (i.e. a new view of the cell is being added or removed on a condition applied). if reloadRowsAtindexPath
is called on the button click then the tableview randomly scroll down to some other row in the tableview. If only the cell content is updated on button click then the layout is not updated properly.
If anybody has faced the same issue with the reload?
Upvotes: 2
Views: 1532
Reputation: 208
This was an issue with the estimated row height. changing the value to some other estimate fixed the issue of unwanted scrolling.
Mentioned in apple docs: "Additionally, try to make the estimated row height as accurate as possible. The system calculates items such as the scroll bar heights based on these estimates. The more accurate the estimates, the more seamless the user experience becomes."
Upvotes: 0
Reputation: 1957
Well this turned out trickier than I expected.
Here's how I implemented it. I am not really sure if this is a good way or not so take it with a grain of salt. Find link to the project below.
You need to know two things:
- The default/normal cell height (which is basically the estimated height of cell).
- Increase in height of cell after the view has been added to the cell (in my example I have taken the height to be 200).
When you tap on a button which is supposed to add the subview you call a completion handler passing the indexPath
and heightToBeAdded
(200 in this example) as parameters.
The completion will look something like this:
self.indexPath = iPath
self.cellHeight = self.defaultCellHeight + heightToBeAdded
UIView.beginAnimations("aaa", context: nil)
UIView.setAnimationDuration(1)
self.tableView.beginUpdates()
self.tableView.reloadRows(at: [iPath], with: .none)
self.tableView.endUpdates()
UIView.commitAnimations()
if heightToBeAdded > 0.0 {
self.addCellSubviews()
}
The iPath
is the same indexPath that you sent a parameter. The cell height is calculated from the two things I have described above.
When endUpdates()
calls the delegate and datasource methods would encounter the following method:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
guard let iPath = self.indexPath else { return UITableViewAutomaticDimension }
if indexPath == iPath {
return cellHeight
}
else {
return UITableViewAutomaticDimension
}
}
This will increase the cell height. But what about the actual subview that needs to be added?
For that we have addCellSubviews()
which is actually a completion block that is executed inside the UITableViewCell subclass. Call this only after end updates because by that time cell height will be calculated properly.
let yView = UIView()
yView.backgroundColor = .yellow
yView.translatesAutoresizingMaskIntoConstraints = false
self.addAsSubview(sView: yView)
NSLayoutConstraint.activate([
yView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 8),
yView.topAnchor.constraint(equalTo: self.topAnchor, constant: 8),
yView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -275),
yView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -8)
])
UIView.animate(withDuration: 1, animations: {
self.layoutIfNeeded()
})
Please note there are some caveats(like tapping one cell closes the other) which I am sure you will be able to work out.
Upvotes: 1