2

Whilst developing my latest app I would occasionally experience a rendering problem with my tableview where it appeared that my tableview cells weren't given enough space to fit in each section.

I struggled to reliably reproduce the issue until recently and after exhausting everything I could think of I decided to clone the fundamentals of the bug into a test app which I managed to finally reproduce a similar issue with it and so I finally tracked down what appears to be the cause of the bug.

My main app uses custom section header views and requires automatic height dimension rows and section headers as I include dynamic content that includes text that can grow and go multiline if required in both of them. When debugging the issue in my main app I quickly tried replacing my custom section header views with default tableview section titles and was still able to reproduce the issue so this test app is using section titles with automatic sectionHeaderHeights which I realise is strange, but the same issue occurs with custom views its just using plain titles leads to a simpler example.

I've uploaded a copy of my project in a zip file located here.

As well as two videos showing the workflow in my test app working fine, and then being broken by the automatic sectionHeaderHeight. These videos are here

The following images show the difference between the App working correctly displaying the 2 test sections and their rows

Working Screenshot

and then broken with the floating "section 2" header occluding one of the rows within section 2.

Broken screenshot

The actual relevant code for this is within the TableViewController in the project

class TableViewController: CoreDataTableViewController<RowEntity>
{
  private let setAutomaticDimensionSectionHeaderHeight=true

    override func viewDidLoad()
    {
      super.viewDidLoad()

      // Get dynamic section heights working

      // its this that seems to be causing the rogue header bug.. setting our section height to automatic dimension!
      if (setAutomaticDimensionSectionHeaderHeight)
      {
        tableView.sectionHeaderHeight = UITableViewAutomaticDimension // this must be the default as dynamic section headers work without this so long as there's an estimated section header height specified
      }
      tableView.estimatedSectionHeaderHeight = 50.0 // seems like this property is absolutely crucial to getting auto layout sized section headers working.. probably need something equivalent set to get cells to do the same?

      // Do any additional setup after loading the view, typically from a nib.
      createSectionsIfNeeded(context: AppDelegate.viewContext)

      createFetchRequest()
    }

You can see that the class constant setAutomaticDimensionSectionHeaderHeight can be set to enable the tableView.sectionHeaderHeight to be initialised to UITableViewAutomaticDimension or not. If it is set to automatic then the bugs as shown in the above videos occur, namely that

  • When toggling the sections visibly using the bar button
    • a new "section 2" header appears immediately in its desired position
    • the existing "section 2" header and its rows all slide down through it into correct position
  • When toggling the sections non-visibly by
    • going to the next view controller (by selecting a table row) and using its toggle tableview sections button
    • and returning back to the tableview
    • repeat this such that you have hidden section 1 and its rows whilst the tableview was offscreen
      • reveals a dummy "section 2" header floating in the position it was in when section 1 was added.
      • This dummy header is attached to the tableview and scrolls with it, but does NOT push other real section headers out of the way when you scroll it to the top of the view

The animation glitch is something I only noticed within this test app as in my main app I always reproduce the change to tableview offscreen in some child view in the navigation controller. The floating header view has been spotted though as described and makes my app appear quite broken and actually breaks it quite badly by hiding some tableview rows. When I first saw it I didn't realise the header was wrong and floating I presumed it was some other issue with my tableview.

I'll probably log a radar on this with Apple as it seems pretty broken but thought i'd see if anyone had any ideas. I've seen some people talk about general issues with automatic section heights and recommending that you just calculate the sizes of the section heights dynamically.. If i can trust auto layout to have resolved at this point then I presume this is something I can try to do by I guess adding up the heights of the component views within my sectionView?

Other than that I've just seen some other question/answer series such as

That talk about perhaps trying to set estimatedSectionHeaderHeight (and estimatedRowHeight) to 0, which I've just tried in my test app and it does seem to "fix" the issue. The latter link also quotes early iOS 11 release notes

Table views now use estimated heights by default, which also means that cells and section header/footer views now self-size by default. The default value of the estimatedRowHeight, estimatedSectionHeaderHeight, and estimatedSectionFooterHeight properties is now UITableViewAutomaticDimension, which means the table view selects an estimated height to use. You should still provide a more accurate estimate for each property if possible, which is your best guess of the average value of the actual heights. If you have existing table view code that behaves differently when you build your app with the iOS 11 SDK, and you don’t want to adopt self-sizing, you can restore the previous behavior by disabling estimated heights by setting a value of zero for each estimated height property. (30197915)

Which made me try just commenting out the estimatedSectionHeaderHeight line all together to use the new default iOS 11 behaviour. This also seemed to work ok and by that description sounds more preferable as I know I do want self sizing. The notes though say that you should provide estimates yourself, which when I do that does lead to these bugs so seems bad advice from apple at this stage.

I'll need to put my main app back to normal again and see if this will fix the issue there. I do remember that when first getting dynamic height sections and rows working that having these estimated properties set to something seemed to be required to actually get my auto layout and multiline text labels working properly to fill the space required (I could have been mistaken at the time though as I was wrestling with all parameters trying to get things working). It just seems wrong to set this to 0 when I can have a good idea at what the estimated size would typically be for these sections, leaving it blank to adopt the default self sizing seems right, though I guess if I release on earlier iOS that code may need tweaking as probably wouldn't work very well.

Any more ideas on this would be really appreciated!

Cheers!

Edit

Upon further testing it appears I was too optimistic with the setting of heights and estimated heights to 0 fixing things. I can get the issue fixed only by not having dynamic height section headers which won't fit my main app use case

I went through and tested all the combinations of the various options with the following results

From a sectionHeaderHeight perspective, with different estimatedSectionHeaderHeights

  • tableView.sectionHeaderHeight = 0
    • no headers with estimated height == 0, 45 or automatic dimension
  • tableView.sectionHeaderHeight = 60
    • headers all with constant 60 height, no UI bug
  • tableView.sectionHeaderHeight = UITableViewAutomaticDimension
    • no headers, or viewForHeaderCalled when estimated headerheight is 0
    • headers with bug if estimated headerheight set to a number, or automatic dimension

from an estimated sectionHeaderHeight perspective with different sectionHeaderHeights

  • tableView.estimatedSectionHeaderHeight = 0
    • no headers with sectionHeaderHeight set to 0 or automatic dimension no viewForHeader called.
    • headers no bug with headerHeight=60
  • tableView.estimatedSectionHeaderHeight = 45
    • no headers with sectionHeaderHeight == 0
    • headers fixed to 60 with no bug with sectionHeaderHeight=60
    • headers auto sized with bug with sectionHeaderHeight==Automatic Dimension
  • tableView.estimatedSectionHeaderHeight = UITableViewAutomaticDimension
    • headers with bug if sectionHeaderHeight==0, Automatic Dimension
    • fixed headers at 60 with no bug with sectionHeaderHeight=60

Basically no combination fixes this issue yet preserves the ability to have dynamic height section headers

jimbobuk
  • 1,211
  • 12
  • 25
  • I'm trying to write this up into a bug for apple.. for now though I'm going to work around it by deleting my tableview's fetchedResultsController in viewDidDisappear() so that when I perform offscreen changes that affect the table it won't update anything so won't leave any rogue headers left behind. It will still animate onscreen badly as I've shown but this is the lesser of the two evils. Hopefully its something that Apple can fix. – jimbobuk Feb 11 '18 at 13:43
  • thanks for writing this all up. I'm very sporadically experiencing what I believe might be the same issue you describe. Do you have any other insights since you wrote this up a year ago or was removing the fetchedResultsController on viewDidDisappear your final workaround? – Brian M Apr 20 '19 at 16:58
  • I don’t think I revisited this issue since writing it up on here. TBH other priorities has stopped me working on the project that this issue was occurring in for a good while and will continue to block me for the foreseeable. I believe this workaround did fix the problem though, no matter how clumsy it seems to be – jimbobuk Apr 20 '19 at 18:37
  • Removing the fetchedResultsController wasn't a great option for me, so I ended up apply this hack: https://stackoverflow.com/a/30279772/7965564. Works well. Wish I could figure out how to fix the actual bug, but I'm moving on... Thanks again for writing up your question. I'm surprised there aren't more threads about this bug - it's been around for years it looks like! – Brian M Apr 24 '19 at 19:41

0 Answers0