'How to remove a Static Cell from a UITableView designed in StoryBoard
The solution is probably very simple, but I couldn't just find it .. !
Working with storyboard (iOS 5), I have a tableViewController, and a designed STATIC tableview with 5 sections, with differents static cell inside each section.
My question is: How to delete a cell programatically in the viewWillAppear?
For example, I have a cell designed for a date
IBOutlet UITableViewCell * cellForDate;
And.. if there's not date, I want to remove my cell.
cellForDate.hidden = true; //Hide the cell, but leave a blank space
Ive tried [tableView deleteRowsAtIndexPaths...] didn't work
Anyone got an idea?
Solution 1:[1]
Hopefully this is a bit more of a universal solution, but it's still not perfect
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
if (cell.hidden) {
return 0;
} else {
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
}
With this method you set your IBOutlet bound cell to be hidden, and then when the table tries to display a row, it first checks to see if the cell (using super call to get the cell) should be hidden, if so it returns a height of 0 which effectively removes it from the list. If the cell shouldn't be hidden then it gets the height from the super so whatever would normally happen still happens.
The only issue with this is that start and end cells are responsible for the divider at the top and bottom of the list or group. So if you hide the last cell in a group the bottom divider on the group will be inset slightly like it is on normal rows rather than full width. This is only a problem if you're hiding the top or bottom rows AND you are using a divider AND you care about totally standard display. The best solution in my case was simply to not hide the top or bottom rows. I moved any content that might be hidden to the middle of the group. Alternatively you could just accept that it isn't quite standard, or disable row dividers on your table.
Solution 2:[2]
Swift 3
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.section == 0 {
cell.isHidden = true
}
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.section == 0 {
return 0
} else {
return super.tableView(tableView, heightForRowAt: indexPath)
}
}
Solution 3:[3]
If you want to permanently delete the tableview cell, just call deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone] with the an indexPath corresponding to your row. You will also need to decrement the number of rows returned by numberOfRowsInSection:.
However, I was looking for a way to temporarily "delete" unwanted static tableview cells from a static tableview, because I wanted a particular row to be there sometimes, and other times not. I also wanted to be able to update this on demand from outside of the tableview controller.
My case is fairly simple, since it is the first row that was being either shown or hidden. You can generalize to suit your own needs. My tableview controller is my data source delegate.
First, I created a public method in the tableview controller to update the state variables and trigger the redisplay:
- (void)updateSettingsDisplay:(BOOL)hideSetting
{
if (hideSetting == self.hideSetting) {
return; // no need to do anything because it didn't change
}
self.hideSetting = hideSetting;
self.numRows = (hideSetting)? kNumRowsWithSetting : kNumRowsWithoutSetting;
// redisplay only if we're visible
if (!self.viewJustLoaded && (self.navController.visibleViewController == self)) {
[self.tableView reloadData];
}
self.viewJustLoaded = NO;
}
The tableview controller's viewDidLoad looks like:
- (void)viewDidLoad
{
[super viewDidLoad];
// check whether or not to display out-of-coverage tableview cell
self.hideSetting = YES; // initial value; could just as easily be NO
self.viewJustLoaded = YES;
[self updateSettingsDisplay];
}
The tableview's data-source delegate:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return self.numRows;
}
and finally
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// set row to the index of the stored tableView cell that corresponds to the
// cell you want (its location in the static tableview from your storyboard)
NSInteger row = indexPath.row;
if (self.hideSetting) {
// skip drawing setting's tableviewcell (since the setting we're hiding is
// first, we can just increment the row to get the one we want)
++row;
assert(row < kTotalRows); // bounds checking just to convince yourself (remove after testing)
}
// get a new index path since the row field is read-only
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:row inSection:indexPath.section];
// grab the cell from super that corresponds to the cell you want
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:newIndexPath]; // static table
return cell;
}
The trick is that the static cells are always available in [super tableView:tableView cellForRowAtIndexPath:newIndexPath] - so use that for permanent storage of your static tableview cells. Then adjust the number of rows as needed, and map the rows correctly (ie, get the row of the stored cell that corresponds to the cell you want displayed) in your tableview delegate's cellForRowAtIndexPath:.
The updateSettingsDisplay method can be called on your tableview controller by any class that retains it. If the tableview controller is not visible when it is called, it will just update the state and wait until next time it becomes visible to change the display.
Solution 4:[4]
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if(indexPath.section == 1) { //For example
return 0
}
}
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if(indexPath.section == 1) {
cell.hidden = true
}
}
Solution 5:[5]
you might want to look at this
https://github.com/xelvenone/StaticDataTableViewController/
usage
self.hideSectionsWithHiddenRows = YES; //YES, NO
[self cell:self.outletToMyStaticCell1 setHidden:hide];
[self cell:self.outletToMyStaticCell2 setHidden:hide];
[self reloadDataAnimated:YES];
Solution 6:[6]
FYI (would comment if I could), if you are overriding a Grouped static UITableView from the Storyboard, then you'll also need to override the Header & Footer views & heights for each section you are modifying/hiding.
I also needed to set the return value for the "GetHeight" methods to something other than zero - implying that a zero value means something else (autolayout, or "not set" or the like).
The below code is in Xamarin iOS C#, but translates directly to the appropriate Obj-C methods:
public override void WillDisplayHeaderView(UITableView tableView, UIView headerView, nint section)
{
if (section == 0)
{
headerView = new UIView(new CGRect(0,0,0,0));
}
}
public override nfloat GetHeightForHeader(UITableView tableView, nint section)
{
if (section == 0)
{
return 1f;
}
return base.GetHeightForHeader(tableView, section);
}
public override void WillDisplayFooterView(UITableView tableView, UIView footerView, nint section)
{
if (section == 0)
{
footerView = new UIView(new CGRect(0, 0, 0, 0));
}
}
public override nfloat GetHeightForFooter(UITableView tableView, nint section)
{
if (section == 0)
{
return 1f;
}
return base.GetHeightForFooter(tableView, section);
}
Solution 7:[7]
Just calculate the numberOfRows(inSection) to remove some rows you don't want
Solution 8:[8]
You need to put beginUpdates before the delete call and endUpdates after:
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:r inSection:s]]];
[tableView endUpdates];
In response to your comment:
- (NSInteger)tableView:(UITableView)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger ret;
switch (section) {
case 0:
// sectionZeroRows doesnt have to be a
// property (an attribute works just fine), but it
// helps with explaining this example.
ret = self.sectionZeroRows;
break;
// ...
}
return ret;
}
- (void)someMethod {
--self.sectionZeroRows;
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]]];
[tableView endUpdates];
}
Solution 9:[9]
// just authed with facebook, and I want to delete the first 3 rows of my 4 row table that are no longer necessary
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
if(wantsFacebook)
return 1;
return 4;
}
After committing the updates, the tableView will call the numberOfRowsInSection delegate to get the updated number of rows and it MUST be the correct sum/difference between the rows when you started the insert/delete and the rows when you ended the insert/delete
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | ima747 |
| Solution 2 | Henry |
| Solution 3 | BeccaP |
| Solution 4 | Yaman |
| Solution 5 | Peter Lapisu |
| Solution 6 | Coruscate5 |
| Solution 7 | SwiftiSwift |
| Solution 8 | |
| Solution 9 | scootklein |
