I have a grouped UITableView where not all sections may be displayed at once, the table is driven by some data that not every record may have. My trouble is that the record
I solved the issue by setting tableView's dataSource and delegate where I initialize the tableView.
Note: remember you might need to make tableView lazy to assign when initializing the tableView.
lazy var tableView: UITableView = {
let tableView = UITableView(frame: .zero, style: .grouped)
tableView.separatorStyle = .none
tableView.backgroundColor = .white
tableView.dataSource = self
tableView.delegate = self
return tableView
}()
There is a convenient value for this case, called CGFloat.leastNonzeroMagnitude
. Use it as follows:
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return .leastNonzeroMagnitude
}
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return .leastNonzeroMagnitude
}
You can also use tableView.sectionHeaderHeight = 0
and tableView.sectionFooterHeight = 0
, but this will be applied for all headers and footers. With the delegate solution you're still able to manipulate different headers' and footers' height, but use .leastNonzeroMagnitude
as a default
I have a similar situation and my data model really works better if I can let it have empty sections.
Your problem can be solved if you set self.tableView.sectionHeaderHeight = 0;
and self.tableView.sectionFooterHeight = 0;
in your tableview controller. These must be 0 because the delegate methods somehow ignore a return value of 0. Then you have to override some delegate methods:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
if (section==0)
return sectionGapHeight;
if ([self tableView:tableView numberOfRowsInSection:section]==0) {
return 0;
}
return sectionGapHeight;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
if (section==[self numberOfSectionsInTableView:tableView]-1) {
return sectionGapHeight;
}
return 0;
}
- (UIView *)sectionFiller {
static UILabel *emptyLabel = nil;
if (!emptyLabel) {
emptyLabel = [[UILabel alloc] initWithFrame:CGRectZero];
emptyLabel.backgroundColor = [UIColor clearColor];
}
return emptyLabel;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
return [self sectionFiller];
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
return [self sectionFiller];
}
This code will also make the gap between sections the same height as the gap before the first section and the gap below the last. That looks better in my opinion than the default where the gap between is twice as big as the ones at the top and the bottom.
The key is to set the table view's sectionHeaderHeight
and sectionFooterHeight
to zero.
You can do this either in viewDidLoad
, or in the User Defined Runtime Attributes section in your storyboard/xib. For some reason, in the Size Inspector tab, you cannot set the header or footer height below 1.
I found that once I did this, I didn't need to override any other delegate methods in a special way. I simply return 0
from numberOfRowsInSection
and nil
from titleForHeaderInSection
for sections that are empty, and they take up no space in my table view.
Sections that do have titles are automatically given the proper header size, and the top of the table view still has the default blank space.
Here is the most elegant solution I've found based on all the answers here, plus this answer:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if ([tableView.dataSource tableView:tableView numberOfRowsInSection:section] == 0) {
return nil;
} else {
// Return title normally
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
if ([self tableView: tableView numberOfRowsInSection: section] == 0) {
return 0.01f;;
}
return UITableViewAutomaticDimension;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
if ([self tableView: tableView numberOfRowsInSection: section] == 0) {
return 0.01f;;
}
return UITableViewAutomaticDimension;
}