Grouped UITableView shows blank space when section is empty
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 records that don't have certain sections show up as blank spaces in the table. There are no footers/headers. Anything I've missed?
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [self getRowCount:section];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [NSString stringWithFormat:@"section: %d row: %d",indexPath.section,indexPath.row];
// Confi开发者_如何转开发gure the cell...
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
float height = 0.0;
if([self getRowCount:indexPath.section] != 0){
height = kDefaultRowHeight;
}
return height;
}
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.
Here is the solution that worked for me
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
switch (section) {
case 1: case 3: case 5: return 20.0f; break;
default: return 0.01f; // for some reason 0 is not accepted - give something :)
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
switch (section) {
case 1: case 3: case 5: return 20.0f; break;
default: return 0.01f; // for some reason 0 is not accepted - give something :)
}
}
In the above screenshot, is your numberOfSectionsInTableView:
method returning a value of 5? And then is your getRowCount:
method (called from numberOfRowsInSection:
) returning a value of 0 for those "missing" sections (e.g. 1, 3 and 4)?
If you don't want gaps in between, the likely solution is to only declare the number of sections you want to be visible. In your example above, you'd only want to return value of 3 from numberOfSectionsInTableView:
.
This solution is based on user362178 solution. But keeps the labels of the sections intact:
Define somewhere at the top of your code:
#define HEIGHT_FOR_HEADER_AND_FOOTER 50
Add to the view did load:
self.tableView.sectionHeaderHeight = 0;
self.tableView.sectionFooterHeight = 0;
Add these methods:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
// Needed because if this does not return a value, the header label of the section will
// be at the top of the screen.
if (section == 0)
{
return HEIGHT_FOR_HEADER_AND_FOOTER;
}
if ([self tableView:tableView numberOfRowsInSection:section] == 0)
{
return 0;
}
else
{
return HEIGHT_FOR_HEADER_AND_FOOTER;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
// if last section
if (section == [self numberOfSectionsInTableView:tableView] - 1)
{
return HEIGHT_FOR_HEADER_AND_FOOTER;
}
return 0;
}
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;
}
I had the same problem in iOS 7 and finally I solved this checking the Height for Header and Footer sections, as Ramesh said:
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
if ([[self.tableView objectAtIndex:section] count] == 0)
return 0.01f;
else
return 30.f;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 0.01f;
}
It's curious, but if you set 0.f as the height the sections is showed with a default size and moves down the other sections.
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 believe that with section table views, each section must exist, and each section has buffer room for a header and a footer regardless of you putting text in them. What you need to do is remove the sections that aren't being used. In your example, you would remove section 1, 3, and 4. This would mean:
"section 0" is indexPath.section == 0
"section 2" is indexPath.section == 1
"section 5" is indexPath.section == 2
If you code in this fashion, the headers and footers will be removed because the nil cell is removed.
This isn't proper coding of course but I just wanted to give you an idea of the theology. Hope this helps.
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.
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
}()
I solved the issue by setting tableView's dataSource and delegate where I initialize the tableView
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
}()
精彩评论