UITableView Custom Section Header, duplicate issue

Solution found.

The table needed to be reloaded before every change. This way the table is at the latest state before making any changes.

add [self.tableView reloadData]; as the fist entry in the "expandSection" method.

CODE:

- (void) expandSection:(id)sender {

  [self.tableView reloadData];

    if (expandedSection == [sender tag]) {
        expandedSection = -1;
        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
    }else if (expandedSection == -1){
        expandedSection = [sender tag];
        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
    }else{
        [self.tableView beginUpdates];  
        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:expandedSection] withRowAnimation:UITableViewRowAnimationNone];
        expandedSection = [sender tag];
        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:[sender tag]] withRowAnimation:UITableViewRowAnimationNone];
        [self.tableView endUpdates]; 

    }
    //[self.tableView reloadData];
}

I had a similar issue which was caused by using dynamic height cells. I had an expandable custom header view and when I was updating tableView to insert and remove the associated rows of the section (meaning that they were expanding, respectively collapsing), the section header which was a subclass of UITableViewHeaderFooterView was not recycled. So basically a new one was allocated and added over the old one resulting in overlapping views. The cell identifier was properly set so must have been something else. When I removed tableView.sectionHeaderHeight = UITableViewAutomaticDimension and implemented func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat the view got properly recycled and only one header view was displayed for each section.

Another solution that I turned out to actually work was to use UITableViewCell instead of UITableViewHeaderFooterView and when you return in func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? you just return cell.contenView, this will work because the method requires to return a UIView and since the contentView of the UITableViewCell is a UIView it works just fine. The idea behind is to use the recycling mechanism of UITableView through UITableViewCell and just return its content after you configure it.

Conclusion. The problem it is very possible to be caused by UITableViewHeaderFooterView when is used with self sizing tableView cells and UITableViewAutomaticDimension instead of manually calculating cell height.