UIPickerView inside static UITableViewCell animation on cell height update issue - ios

I have a static UITableView in which I have a UIPickerView inside a UITableViewCell. When triggering didSelectRowAt it is supposed to toggle the height of the cell that has the UIPickerView. I use beginUpdates() and endUpdates() to change the height of the cell. It looks right when expanding but when collapsing, the UIPickerView doesn't animate and collapses faster than the cell does. Not sure what I am doing wrong.
I have made sure all views are set to true for clipToBounds on the UITableViewCell, ContentView (of the UITableViewCell) and UIPickerView. I have tried to wrap the beginUpdates/endUpdates in a DispatchQueue.main.async. My UIPickerView is using AutoLayout and (leading, trailing, top, bottom) edges are equal to Content View of the cell. I am using a StoryBoard for my UI.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch (indexPath.section, indexPath.row) {
case (1,1):
showPicker = !showPicker
tableView.beginUpdates()
tableView.endUpdates()
default:
()
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch (indexPath.section, indexPath.row) {
case (1, 1):
if !showDueDate {
return 0
}
case (1, 2):
if !showPicker {
return 0
}
case (_, _):
break
}
return super.tableView(tableView, heightForRowAt: indexPath)
}
I expected the animation to be smooth and UIPickerView to collapse alongside the UITabeViewCell.
see gif to see issue via GIPHY

Use self.tableView.reloadRows(at: IndexPath(row: indexPath.row, section: indexPath.section), with: .fade) instead of beginUpdates/endUpdates. It will give you a smooth animation when animating table changes.

Figured it out. My bottom constraint of the UIPickerView was set to the bottom of the content view, constant was 0. Changed it to <= 0 and it fixed it. Don't understand why 0 didn't work. Atleast I figured it out...

Related

iOS Collection View shifts up when data reloads

I have a TableView that manages two rows of data. Each row is managed by a CollectionView which allows the user to scroll horizontally and filter the data. The filters are part of the table view and above the CollectionView.
Here's a visual:
When I click the filters along row one (off screen in the image above) the ui smoothly updates the data. However when I click the filters along row two the collection view shifts up briefly before back down to the proper position.
I'm fairly confident that the issue has to do with my interface builder configuration but for the record, this is the code that reloads the collection views.
func onFetchCompleted() {
if shouldRefreshRow() {
//tableView.reloadData() // reload all rows
tableView.reloadRows(at: [IndexPath(row: viewModel!.rowToFetch!, section: 0)], with: .none)
}
}
func shouldRefreshRow() -> Bool {
return self.viewModel?.businessesStore[viewModel!.rowToFetch!].previousPage == 1
}
And here's the TableView config:
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath) as? BusinessesTableViewCell {
cell.configureCell(dataSourceDelegate: self, filterDelegate: self, forPath: indexPath, indexPathsToReload:
return cell
}
return UITableViewCell()
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if(viewModel?.businessesStore[collectionView.tag].businesses.count ?? 0 > 0) {
return (viewModel?.businessesStore[collectionView.tag].total)!
}
return 0
}
In interface builder I have my table view leading, trailing and bottom constraints set to 0 with respect to the superview and top to 0 with respect to the view above the table view (the solid black one in the image above).
I'm fairly certain this is an interface builder issue because If I remove my bottom constraint and set my TableView height to something like 400, the view behaves properly when a filter is clicked. The catch is that the user has to be scrolled down to the bottom of the screen otherwise it behaves in the janky manner explained above.
Here's the layout:
Any ideas?
I was able to find a solution to this issue after quite a bit of persistence. I had two things wrong in my configuration.
1: When I was reloading data with this line of code tableView.reloadRows(at: [IndexPath(row: viewModel!.rowToFetch!, section: 0)], with: .none) it seems to be including some sort of bounce animation on the collection view. I replaced that with tableView.reloadData() which fixed most of the bounce that was occurring.
2: I increased the size of my table view rows which removed the rest of the bounce.

Swift animate UITableViewCell expanding height constraint [duplicate]

This question already has an answer here:
Collapsable Table Cells [Swift 3] [duplicate]
(1 answer)
Closed 5 years ago.
I have a TableView with a static cell inside which I would like to expand when a button has been pressed. There is a container view inside the cell, the constraints have been set up correctly but I am not sure how to actually animate the cell expanding as it requires me to refresh the table view to update the constraints and expand the cell.
Currently when I call expandContainerView it doesn't animate, because I am calling self.tableView.reloadData.
Here is the code that I have used to expand the cell
#objc private func expandContainerView(notification: NSNotification) {
self.view.layoutIfNeeded()
UIView.animate(withDuration: 2.0, delay: 0.0, options: .curveEaseOut, animations: {
self.containerHeightConstraint.constant = 410
self.view.layoutIfNeeded()
self.tableView.reloadData()
}, completion: nil)
}
And here is my height for each row at index code
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
Try without an animation block:
containerHeightConstraint.constant = 410
tableView.beginUpdates()
tableView.endUpdates()
You can only reload cell which you want to expand using below code. I added in the didSelectRowAt but you can add same code in button action method.
Set expandCell variable to true for changing height of cell when reloading.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// Expand View
self.expandCell = true
self.tableView.beginUpdates()
self.tableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
self.tableView.endUpdates()
}
You need specify height to expand cell view else it will show default height.
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == expandRowIndex && self.expandCell {
return 200
}
return UITableViewAutomaticDimension
}
Note : No need of animation for this. Expansion of view animation at time of reloading cell

UITableView reload animation doesn't work properly

I have a table view which reloads it data with animation on button tap. It was working great until there was one label with text.
I use this code to reload data with animation for one button (I have only one section):
tableView.reloadSections(IndexSet(integersIn: 0..<tableView.numberOfSections), with: UITableViewRowAnimation.right)
and this for another button:
tableView.reloadSections(IndexSet(integersIn: 0..<tableView.numberOfSections), with: UITableViewRowAnimation.left)
And on the top of table view it works okay, but in the middle or in the end it scrolling.
Link for gif - https://giphy.com/gifs/l0DAHUyzm7BMGnKrm/html5
Then I added this code for reloading with animation:
let offset = tableView.contentOffset
tableView.beginUpdates()
tableView.reloadSections(IndexSet(integersIn: 0..<tableView.numberOfSections), with: UITableViewRowAnimation.left)
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(offset, animated: false)
Same code for .right animation. It fixed scrolling, but added another issue. Again on top of table view it works okay, but then... Watch gif please.
Link for gif - https://giphy.com/gifs/xT1R9Gfaa2po6dMf2U/html5
I'm using test data to fill table view, not fetching or something else.
Hope for help, thanks
EDIT:
I found that if I set standard cell height from code animation works nice, only in this case it works:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 44.0
}
I found the problem. The problem was in cell height, animation was starting for estimated cell height in 44.0 and my cell height is 117.0. So this code fixed my problem:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 117.0
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return 117.0
}

TableView Auto Scrolling misbehaving after adding cells

View Setup:
My TableView has 3 sections with 4 or 9 cell each. Each Cell has a Label and TextField.
On Starting to edit a cell at index 2 of each section, I reload the section which will now consist of 9 cells(update model to dequeueCell so that 5 more cells will be added).
Problem:
The tableView scrolls as expected(brings textfield to visible part of the screen) for the unexpanded state of the section. But after I add cells by beginning to edit the textfield of cell at index 2 of any section, the tableView scrolls such that it hides the textfield. The weird scrolling occurs for any cells in the tableview once any section has expanded numbers of cells. Also, while weird scroll is happening, the tableView is reloaded(which is leading to lose the focus away from textfield). I have included tableView.reloadSection(_:) in the didBeginEditing(:_) custom delegate of the cell.
I have seen this problem in iOS 9 and 10
Sorry for poor explanation. Thanks
Heres the Github Repo
And Problem is here
P.S. I am using Swift 3 and Xcode 8.3.3 with deployment target iOS 10
Please do not suggest answer in Swift 4 and Xcode 9
You can try another approach: change the height of cells instead of insert / delete.
Change number of cells to always return all items:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
guard let sectionEnum = Sections(rawValue: section) else { return 0 }
return sectionEnum.getRows(forExpanded: true).count
}
Set height of 'hidden' items to 0:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
guard let sectionEnum = Sections(rawValue: indexPath.section) else { return 0 }
let isExpanded = expandedSectionData[indexPath.section]
if (!isExpanded) {
let object = sectionEnum.getRows(forExpanded: true)[indexPath.row]
if (!sectionEnum.getRows(forExpanded: false).contains(object)) {
return 0;
}
}
return self.tableView.estimatedRowHeight
}
Set cell to clip subviews to its bounds:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
....
cell.clipsToBounds = true;
return cell
}
And change updating code to (remove tableView.reloadSections, change indexPath):
func didBeginEditing(textField: UITextField, cell: UITableViewCell) {
guard let indexPath = tableView.indexPath(for: cell), let section = Sections(rawValue: indexPath.section) else { return }
if indexPath.row == 7 && !expandedSectionData[indexPath.section] {
expandedSectionData[indexPath.section] = true
tableView.beginUpdates()
tableView.endUpdates()
tableView.scrollToRow(at: indexPath, at: UITableViewScrollPosition.none, animated: true)
textField.becomeFirstResponder()
}
}
You need to make textfield as first responder again, after reloading section text field no longer remains first responder.
You might need to change something like -
func didBeginEditing(textField: UITextField, cell: UITableViewCell) {
guard let indexPath = tableView.indexPath(for: cell) else { return }
if indexPath.row == 2 && !expandedSectionData[indexPath.section] {
tableView.beginUpdates()
expandedSectionData[indexPath.section] = true
tableView.reloadSections(IndexSet(integer: indexPath.section), with: .automatic)
tableView.endUpdates()
// after tableview is reloaded, get cell again
let cell = tableView.cellForRow(at: IndexPath(row: 2, section: indexPath.section)) as? TestCell
cell?.textField.becomeFirstResponder()
}
}
I have tried running this, kind of looks fine to me.
This issue has to do with your use of self-sizing tableview cells. To fix the issue, comment out these two lines in your viewDidLoad and consider defining the height of your cells with tableView:heightForRowAtIndexPath:.
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100
Since the self-sizing tableview documentation states,
To define the cell’s height, you need an unbroken chain of constraints
and views (with defined heights) to fill the area between the content
view’s top edge and its bottom edge
I also tried changing the bottomMargin = textField.bottom constraint from priority 750 to 1000, but this did not fix the issue.

Animating UIView with Autolayout animates UITableView's checkmark

I have strange behaviour. My whole view controller looks like this (vertical order):
UILabel,
UITableView,
UIView (that will slide in&out when needed). [see p.s.]
I added constraint between UIView and bottom layout guide and I'm animating it with this method:
func toggleContinueButton(_ toggleOn: Bool) {
if toggleOn {
self.buttonViewConstraint.constant = 0
} else {
self.buttonViewConstraint.constant = -80
}
UIView.animate(withDuration: 0.5) {
self.view.layoutIfNeeded()
}
}
For some strange reason this screws checkmark, which is now "flying" from outside of left margin of screen and right into correct and expected position, on the right side of selected cell. Belowe I have simple code for selecting/deselecting:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)!.accessoryType = .checkmark
self.updateContinueButtonState()
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
tableView.cellForRow(at: indexPath)!.accessoryType = .none
self.updateContinueButtonState()
}
Has anybody ever met such weirdness?
p.s. tried two versions: first, where sliding UIView is hovering in&out on top of tableView, and second where sliding in UIView is shrinking tableView's height. None worked.
Its hard to say, but you might try calling layoutIfNeeded first, then changing the constraint, then calling it again inside the animation block. That gives other layout changes time to update first.

Resources