18

I have a table view form created using Static Cells in IB/Storyboard. However, I need to hide some of the cells at runtime depending on certain conditions.

I have found a few 'answers; to this question on SO, e.g.

UITableView set to static cells. Is it possible to hide some of the cells programmatically?

.. and they focus on setting the height of the cell / row to 0. This is great, except I now get exceptions from AutoLayout because the constraints can't be satisfied. How do I get around this last problem? Can I temporarily disable Auto-Layout for a subview? Is there a better way to be doing this in iOS7?

Community
  • 1
  • 1
ConfusedNoob
  • 9,826
  • 14
  • 64
  • 85

9 Answers9

14

I found the best way to do this is to simply handle the numberOfRowsInSection, cellForRowAtIndexPath and heightForRowAtIndexPath to selectively drop certain rows. Here's a 'hardcoded' example for my scenario, you could do something a little smarter to intelligently remove certain cells rather than hard code it like this, but this was easiest for my simple scenario.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
    if (indexPath.section == 0 && hideStuff) {
        cell = self.cellIWantToShow;
    }
    return cell;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    CGFloat height = [super tableView:tableView heightForRowAtIndexPath:indexPath];
    if (indexPath.section == 0 && hideStuff) {
        height = [super tableView:tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]];
    }
    return height;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSInteger count = [super tableView:tableView numberOfRowsInSection:section];

    if (section == 0 && hideStuff) {
        count -= hiddenCells.count;
    }

    return count;
}
ConfusedNoob
  • 9,826
  • 14
  • 64
  • 85
  • Glad you sorted this, I was feeling guilty about my old "answer" not working! – jrturton Sep 23 '13 at 06:27
  • 4
    This may work but I think it's unsupported, as Apple's Table View Programming Guid says "If a table view in a storyboard is static, the custom subclass of UITableViewController that contains the table view should not implement the data source protocol." (from https://developer.apple.com/library/ios/documentation/userexperience/conceptual/tableview_iphone/CreateConfigureTableView/CreateConfigureTableView.html#//apple_ref/doc/uid/TP40007451-CH6-SW27 ). These methods are all from the data source protocol. In other words, there's no assurance this solution won't break when Apple upgrades things. – algal Nov 28 '13 at 19:10
  • This seems to fail in iOS 8. Console spits out a warning, that the delegate is implemented for static table view and this is probably by error and it will ignore the delegate. Thank you, Apple! – osxdirk Sep 22 '14 at 12:57
8

Hide the cells on the storyboard and set the height to 0:

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
    let cell: UITableViewCell = super.tableView(tableView, cellForRowAtIndexPath:indexPath)
    return cell.hidden ? 0 : super.tableView(tableView, heightForRowAtIndexPath:indexPath)
    }
}
Sahil Kapoor
  • 11,183
  • 13
  • 64
  • 87
fede1608
  • 2,808
  • 1
  • 16
  • 17
3

If you ensure there's no constraint touching the bottom edge of the cell, autolayout shouldn't barf (tested on iOS 6.0, 6.1, 7.0). You'll still 'anchor' to the top edge and have to pin the heights. (You can do the reverse and anchor to the bottom, of course.)

If your layout depends on both the top and bottom edge positions, it may be possible to programmatically remove the constraints (they're just objects, after all).

Ian Howson
  • 653
  • 7
  • 14
  • This worked for me. ConfusedNoob's answer would seem more appropriate. But since I'm using Xamarin I couldn't get that to work easily. – m1sk Oct 13 '14 at 09:45
  • A better way is to change your bottom constraint to <= as opposed to ==. – Jordan H Mar 27 '15 at 23:40
3

The simplest way is to change height of sections and rows. It works for me.

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    if (section == 3) {
        return 0;
    } else {
        return [super tableView:tableView heightForHeaderInSection:section];
    }
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
    if (indexPath.section == 3) {
        cell.hidden = YES;
        return 0;
    } else {
        cell.hidden = NO;
        return [super tableView:tableView heightForRowAtIndexPath:indexPath];
    }
}
otello
  • 109
  • 1
  • 2
2

The kosher way to do this is to use dynamic cells, setting the row height to 0 is a hack. Static cells are very convenient but limited in functionality.

GayleDDS
  • 4,443
  • 22
  • 23
  • 2
    To do this with dynamic cells would send the complexity of the form through the roof. I don't believe there isn't a better solution, even if the complexity of the hidden cells is high, the total complexity (and thus maintainability) will still be less than me having this large form rendered via dynamic cells (which I can't see in IB). Unless you know a good way to do this with Dynamic Cells whilst still supporting IB and outlets etc. – ConfusedNoob Sep 23 '13 at 00:21
  • You can still use IB outlets with dynamic cell prototypes in your storyboard. You just need to sub class `UITableCell` and control drag the outlets to the new sub class.Then in your table view controller access the property. – GayleDDS Sep 23 '13 at 02:47
1

I managed to avoid exceptions from Auto Layout by first removing the constraints on the cell's contentView programmatically in viewDidLoad, and then setting that cell's height to 0 in heightForRowAtIndexPath.

Andreas
  • 183
  • 1
  • 10
1

I have found a way that allows you even row animations and is working on iOS 8.3. All you need is to implement the tableView:numberOfRowsInSection: data source method and then add/delete row by UITableView methods insertRowsAtIndexPaths:withRowAnimation: and deleteRowsAtIndexPaths:withRowAnimation:.

Here is example of hiding exact row based on UISwitch state:

- (IBAction)alowNotif:(id)sender {
    UISwitch *sw = (UISwitch*)sender;
    NSIndexPath *index = [NSIndexPath indexPathForRow:5 inSection:0];
    if ([sw isOn]) {
        [self.tableView insertRowsAtIndexPaths:@[index] withRowAnimation:UITableViewRowAnimationAutomatic];
    }
    else {
        [self.tableView deleteRowsAtIndexPaths:@[index] withRowAnimation:UITableViewRowAnimationAutomatic];
    }
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    if (![notifications isOn]) {
        return 5;
    }
    return 6;
}

As was mentioned above by @algal, numberOfRowInSection: is still UITableViewDataSource method, so one does not simply know how long its gonna work.

Fiser33
  • 286
  • 1
  • 10
0

The best way for me was to modify numberOfRowsInSection method. I removed datasource which i did not want to display. Best solution for me, because everything is in one function.

(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if(section==0) {
    NSUInteger productCount = _products.count;
    for(loop trough your data) {
      if(your condition is true)
         productCount--;
      }
    }
    return productCount;
} else
    return self.groups.count;
}
nejcb1
  • 131
  • 1
  • 2
  • 6
0

Implemented this too, for a tableview in a StoryBoard. My cells are embedded in sections with a header, represented by that blue cube in xcode6 IB. Indeed if you implement heightForHeaderInSection, heightForFooterInSection and titleForHeaderInSection,titleForFooterInSection you can access the headers when the table is displayed, and return 0.0 and nil respectively, and return 0 for numberOfRowsInSection.

Basically it all works fine, except that for every cell hidden a ca. 10 pixel high vertical space remains for every cell (section) you hide. Any idea what that could be?

RickJansen
  • 1,615
  • 18
  • 24