Friday, September 29, 2017

TableView Auto Scrolling misbehaving after adding cells

Leave a Comment

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 Problem Demo

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

3 Answers

Answers 1

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

Answers 2

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.

Answers 3

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.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment