5

I have an expandable UITableView. When sections tapped, they expand or collapse with animation (Scroll). My problem is that there is a weird animation when expanding or collapsing headers. UITableView scrolls to top and then goes to the tapped cell. In addition, when there is no expanded cell, sometimes, It doesn't scroll to top and there is a big space between top header and top view of UITableView.

My problem is that I need to scroll to expanded section and also get rid of scroll to top bug.

I tried below solution but didn't work for me: prevent table view to scrolling top after insertRows

It also looks like same problem with below question, but can't figure out how to implement it. Why does my UITableView "jump" when inserting or removing a row?

How I toggle selection:

func toggleSection(header: DistrictTableViewHeader, section: Int) {
    print("Trying to expand and close section...")
    // Close the section first by deleting the rows
    var indexPaths = [IndexPath]()
    for row in self.cities[section].districts.indices {
        print(0, row)
        let indexPath = IndexPath(row: row, section: section)
        indexPaths.append(indexPath)
    }

    let isExpanded = self.cities[section].isExpanded
    if(isExpanded){
        AnalyticsManager.instance.logPageEvent(screenName: analyticsName!, category: "Button", action: Actions.interaction, label: "\(self.cities[section].name) Collapse Click")
    }else{
        AnalyticsManager.instance.logPageEvent(screenName: analyticsName!, category: "Button", action: Actions.interaction, label: "\(self.cities[section].name) Expand Click")
    }
    self.cities[section].isExpanded = !isExpanded

    // This call opens CATransaction context
    CATransaction.begin()
    // This call begins tableView updates (not really needed if you only make one insertion call, or one deletion call, but in this example we do both)
    tableView.beginUpdates()
    if isExpanded {

        tableView.deleteRows(at: indexPaths, with: .automatic)
    } else {
        tableView.insertRows(at: indexPaths, with: .automatic)
    }
    // completionBlock will be called after rows insertion/deletion animation is done
    CATransaction.setCompletionBlock({
        // This call will scroll tableView to the top of the 'section' ('section' should have value of the folded/unfolded section's index)
            if !isExpanded{
                self.tableView.scrollToRow(at: IndexPath(row: NSNotFound, section: section) /* you can pass NSNotFound to scroll to the top of the section even if that section has 0 rows */, at: .top, animated: true)
            }
    })

    if self.scrollToTop(){
        self.tableView.setContentOffset(.zero, animated: true)
    }
    // End table view updates
    tableView.endUpdates()


    // Close CATransaction context
    CATransaction.commit()

}

private func scrollToTop() -> Bool{
    for sec in self.cities{
        if(sec.isExpanded){
            return false
        }
    }
    return true
}

I'm giving height of cell inside;

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 120
}

How I declare headers;

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {

    let header = DistrictTableViewHeader()
    header.isColapsed = !self.cities[section].isExpanded
    header.customInit(title: self.cities[section].name, section: section, delegate: self)
    return header
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 60
}

EDIT: Solution in this question (Setting estimated height to 0) looks like working when inserting row. However, I still have bug when deleting rows. Bottom header goes to center of tableview and then goes to bottom after collapse header.

iOS 11 Floating TableView Header

Emre Önder
  • 2,408
  • 2
  • 23
  • 73

4 Answers4

1

You can try using below code. Just get the last content offset of your tableview. Then do the update and reassign the content offset.

let lastOffset = tableView.contentOffset
self.tableView.beginUpdates()
self.tableView.layer.removeAllAnimations()
self.tableView.endUpdates()
tableView.contentOffset = lastOffset
Hardik Halani
  • 290
  • 2
  • 12
0

Instead of tableView.beginUpdates() and tableView.endUpdates(), In my Code i'm using tableView.reloadData() for expanding and contracting the particular section, You can call reloadData when you need to provide expansion of section.

This results that you don't have the problem of animation scroll to the top. And works fine in my project where I have to show number of rows in particular section on a click of button which includes in that section.

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if section == 0 {
        return 1
    }
// Ignore the If Else statements it's just when do you need expansion of section.
    else {
        if showMore == true {
            return self.userPoints.rewardsData[section - 1].count - 1
        }
        else {
            return 0
        }
    }
}

Also Don't Forget to increase or decrease the number of rows to that particular section accordingly.
Previous line is important to avoid any crash.

Mayank Verma
  • 108
  • 1
  • 8
  • Have you increased the number of rows for expanding that section. And also use LayoutIfneeded inside UIView.animate with duration – Mayank Verma Oct 09 '20 at 04:58
0

Simple Solution swift3 and Above

use below extension as

eg: tableViewOutlet.tableViewScrollToBottom(animated: true)

extension UITableView {

    func tableViewScrollToBottom(animated: Bool) {

        DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {

            let numberOfSections = self.numberOfSections
            let numberOfRows = self.numberOfRows(inSection: numberOfSections-1)
            if numberOfRows > 0 {
                let indexPath = IndexPath(row: 0, section: 0)
                self.scrollToRow(at: indexPath, at: UITableView.ScrollPosition.top, animated: animated)
            }
        }
    }
}
midhun p
  • 1,987
  • 18
  • 24
0

I also facing same issue but after read some tutorials and research & Analysis I got the this issue occurred due to height of cell when you expand the section at that tableview count height of cell from 0 to 120(as per your cell height). In my case I solved that issue using estimated height of cell.

I hope that will help you, Thanks

V D Purohit
  • 1,179
  • 1
  • 10
  • 23