Temporarily Remove un-Editable Cells UITableView - iphone

I have this code implemented in my tableView:
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
return NO;
}
return YES;
}
It does what I want, but I want to go one step better and make "section 0" disappear altogether when the edit button is pressed (this effect can be seen if you go into the "keyboard" menu on iOS and select edit in the top right corner, the top two sections disappear in an animation). I have attempted to temporarily remove the first section, but my app crashes when [tableView reloadData]; is called:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (tvController.editing == YES) {
return 1;
}else if (tvController.editing == NO) {
return 2;
}
return 0;
}
Also, I do not think I will end up with an animation if I get that code working, I think my approach is wrong. Thanks for your help guys!

Your problem
One of your sections is longer than the preceding one.
Since you hide section 0 by reporting 1 less section in numberOfSectionsInTableView:, in editing mode every delegate method must adjust the section number. One of them is not doing so.
// every delegate method with a section or indexPath must adjust it when editing
- (NSInteger) tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
if (tvController.editing) section++;
return [[customers objectAtIndex:section] count];
}
- (UITableViewCell*) tableView:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
{
int section = indexPath.section;
if (tvController.editing) section++;
id customer = [[customers objectAtIndex:section] indexPath.row];
// etc
}
My approach
UITableView reloadSections:withRowAnimation: reloads specified sections with an animation. Call it from your setEding:animated: delegate method.
- (void) setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
UITableViewRowAnimation animation = animated ? UITableViewRowAnimationFade : UITableViewRowAnimationNone;
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:animation];
[self.tableView reloadSectionIndexTitles];
self.navigationItem.hidesBackButton = editing;
}
Your delegate also needs indicate that the hidden section has no rows or title.
- (NSInteger) tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
{
if (self.editing && section == 0) {
return 0;
}
return [[customers objectAtIndex:section] count];
}
- (NSString*) tableView:(UITableView*) tableView titleForHeaderInSection:(NSInteger) section
{
if (self.editing && section == 0) {
return nil;
}
[[customers objectAtIndex:section] title];
}

Related

How do I cover the "no results" text in UISearchDisplayController's searchResultTableView?

I don't want to show the "no results" text while my server is processing a search query.
I figured out the exact coordinates of the table cell that contains the label and attempted to cover it.
self.noResultsCoverView = [[[UIView alloc] initWithFrame:CGRectMake(
0.0,
44.0,
320.0,
43.0
)] autorelease];
self.noResultsCoverView.backgroundColor = [UIColor whiteColor];
[self.searchDisplayController.searchResultsTableView addSubview:self.noResultsCoverView];
To my chagrin, my cover was above the table view, but below the label. I need the cover to be above the label. searchResultsTableView::bringSubviewToFront didn't work, which makes me believe that the label isn't a child of the searchResultsTableView at all.
BTW, this Stack Overflow answer doesn't quite work for me. It works on the very first search, but flashes a weird black cover on subsequent searches.
this should do the work properly. The code to return at least one cell:
BOOL ivarNoResults; // put this somewhere in #interface or at top of #implementation
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
if (filteredList.count == 0) {
ivarNoResults = YES;
return 1;
} else {
ivarNoResults = NO;
return [filteredList count];
}
}
// {…}
// return the unfiltered array count
}
and for "showing" the clean cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (tableView == self.searchDisplayController.searchResultsTableView && ivarNoResults) {
static NSString *cleanCellIdent = #"cleanCell";
UITableViewCell *ccell = [tableView dequeueReusableCellWithIdentifier:cleanCellIdent];
if (ccell == nil) {
ccell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cleanCellIdent] autorelease];
ccell.userInteractionEnabled = NO;
}
return ccell;
}
// {…}
}
The easiest way to work around this is to return 1 in numberOfRowsInSection while the query is in progress and leave the dummy cell empty or set its hidden property to YES so it is not visible.
Try this it worked for me
In the UISearchDisplayController delegate do this:=
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString
{
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.001);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
for (UIView* v in self.searchDisplayController.searchResultsTableView.subviews) {
if ([v isKindOfClass: [UILabel class]] &&
[[(UILabel*)v text] isEqualToString:#"No Results"]) {
[(UILabel*)v setText:#""];
break;
}
}
});
return YES;
}
You need to realize that when you have a UISearchDisplayController, and the search bar is active, the UITableView argument passed into your UITableView data source and delegate methods is in fact NOT your tableView object, but a tableView managed by the UISearchDisplayController, intended to display "live" search results (perhaps results filtered from your main data source, for example).
You can easily detect this in code, and then return the appropriate result from the delegate/data source method, depending on which tableView object is asking.
For example:
- (NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)section
{
if (tv == self.searchDisplayController.searchResultsTableView) {
// return the number of rows in section for the visible search results.
// return a non-zero value to suppress "No results"
} else {
// return the number of rows in section for your main data source
}
}
The point is that your data source and delegate methods are serving two tables, and you can (and should) check for which table is asking for data or delegation.
By the way, the "No results" is (I believe) provided by a background image which the UISearchDisplayController displays when the delegate says there are no rows... You are not seeing a 2-row table, the first blank and the second with text "No results". At least, that's what I think is happening there.

Missing Cell Contents When Adding "Add New Item" row to bottom row of UITableView

I am creating an "add new item" row to a section in a table view which gets dynamically added/deleted as the editing mode is entered/exited. This works fine however if the table is longer than the screen then the labels for any rows not yet displayed are not shown when you scroll down to see the new "add new item row".
The key parts of my code are:
setEditing...
-(void)setEditing:(BOOL)editing animated:(BOOL)animate
{
[super setEditing:editing animated:animate];
[self.tableView setEditing:editing animated:animate];
NSArray *paths = [NSArray arrayWithObject:
[NSIndexPath indexPathForRow:[self.location.rooms count] inSection:kSectionRooms]];
if (editing)
{
[[self tableView] insertRowsAtIndexPaths:paths
withRowAnimation:UITableViewRowAnimationTop];
}
else {
[[self tableView] deleteRowsAtIndexPaths:paths
withRowAnimation:UITableViewRowAnimationTop];
}
}
numberOfRowsInSection...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
switch (section) {
case kSectionFields:
return NUM_SECTION_FIELDS_ROWS;
break;
case kSectionRooms:
return [location.rooms count] + ([self.tableView isEditing] ? 1 : 0);
break;
default:
return 0;
break;
}
return 0;
}
portion of cellForRowAtIndexPath
if ([self.tableView isEditing] && row == location.rooms.count)
{
roomCell.textLabel.text = #"Add new room...";
}
else
{
roomCell.textLabel.text = [[loc.rooms objectAtIndex:row] name];
}
editingStyleForRowAtIndexPath....
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
switch ([indexPath section])
{
case kSectionFields:
return UITableViewCellEditingStyleNone;
break;
case kSectionRooms:
if ([indexPath row] == location.rooms.count && [self.tableView isEditing])
{
return UITableViewCellEditingStyleInsert;
}
else
{
return UITableViewCellEditingStyleDelete;
}
break;
}
return UITableViewCellEditingStyleNone;
}
These are all working correctly if there is no scrolling but as soon as there is scrolling then I get problems. I understand that this must be due to the fact that the TableView has not displayed the hidden rows and therefore has not gone through the lazy loading process. I've tried using reloadData but that makes no difference (unsurprisingly) but as I am new to this I am not sure of the best way of making the rows appear.
I am sure there will be a simple solution so any ideas would be most appreciated!
Cheers in advance
jez

UITableview section editable

there are 5 section in my tableview 1)1 and 2 are not editable while 3,4,and 5 are editable
in section 1 am using checkbox,2 radio box , 3 insert cell , 4 delete cell ,5 move cell
so for last 3 i want my tableview will be come editable and sign of + and - will be shown in starting of cell.
and not for forst 2 section.
i tried - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
but this is not working. any help please?
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
NSInteger section = [indexPath section];
if (section ==0)
return NO;
if (section ==1)
return NO;
if (section ==2)
return YES;
if (section ==3)
return YES;
if (section ==4)
return YES;
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
[self.tableView setEditing:editing animated:animated];
[tableView reloadData];
}
You're on the right track with tableView:canEditRowAtIndexPath:. You'll also want to implement tableView:editingStyleForRowAtIndexPath:.
Also, are you calling setEditing:animated on your UITableView to put the table into editing mode? I assume you are, but it never hurts to double-check.

Display some grouped tableview sections according the state of the first section, how?

I would a grouped tableview such that:
when the only row of the first section is associated to a state, say A, I can see only this first section with, possibly, some text under it (in a footer for example);
when this state changes, I would see other sections under the first;
How could I achieve this? Some code/link to obtain something similar?
Thanks,
Fran
No problem, just add some if else logic in all your tableView Datasource and delegate methods.
For example like this:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (!canUseInAppPurchase || isLoading) {
return 1;
}
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (!canUseInAppPurchase || isLoading) {
return 1;
}
if (section == 0) {
// this will be the restore purchases cell
return 1;
}
return [self.products count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
cell = ...
NSString *cellText = nil;
if (!canUseInAppPurchase) {
cellText = #"Please activate inapp purchase";
}
else if (isLoading) {
cellText = #"Loading...";
}
else {
if (section == 0) {
cellText = #"Restore purchases";
}
else {
cellText = productName
}
}
cell.textLabel.text = cellText;
return cell;
}
and if you want to add or remove the second section you could use a simply [tableView reloadData]; or this smoother variant:
[self.tableView beginUpdates];
if (myStateBool) {
// activated .. show section 1 and 2
[self.tableView insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 2)] withRowAnimation:UITableViewRowAnimationTop];
}
else {
// deactivated .. hide section 1 and 2
[self.tableView deleteSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 2)] withRowAnimation:UITableViewRowAnimationBottom];
}
[self.tableView endUpdates];
be careful, you have to change the data in your datasource first. And this code will add 2 sections. But you can adopt it to your needs easily.

Can't get insertion control in UITableView

I've searched all over the web, and my code seems to be nearly identical to everything out there, but I can't get my UITableView editing to work for inserting a row. When I click the edit button, all of my existing rows get the delete control, but I get no additional row for insertion. I'm pulling my hair out for what seems like it should be a simple thing. What am I missing?
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
[categoryTV setEditing: editing animated:animated];
NSIndexPath *ip = [NSIndexPath indexPathForRow:[[appDelegate appCategories] count] inSection:0];
[self.categoryTV beginUpdates];
if (editing) {
[categoryTV insertRowsAtIndexPaths:[NSArray arrayWithObject:ip] withRowAnimation:UITableViewRowAnimationLeft];
} else {
[categoryTV deleteRowsAtIndexPaths:[NSArray arrayWithObject:ip] withRowAnimation:UITableViewRowAnimationFade];
}
[self.categoryTV endUpdates];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
if (indexPath.row >= [[appDelegate appCategories] count])
cell.textLabel.text = NSLocalizedString(#"New Category", #"NewCategoryCellText");
else
cell.textLabel.text = [[[appDelegate appCategories] objectAtIndex:indexPath.row] detailValue];
....
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (self.editing) {
return [[appDelegate appCategories] count] + 1;
} else {
return [[appDelegate appCategories] count];
}
}
As noted, I forgot to include my version of the suggested method, now shown below. Thanks.
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == [[appDelegate appCategories] count]) {
return UITableViewCellEditingStyleInsert;
} else {
return UITableViewCellEditingStyleDelete;
}
}
You need to implement the following delegate method to assign an editing style:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleInsert;
}
Obviously you will only return that for the cell you want to have an insert control. Otherwise, you will need to return UITableViewCellEditingStyleDelete.
I saw similar behavior in my UITableView, and the problem was that my canEditRowAtIndexPath method returned NO for the insertion control row.
set tableview editing property YES
- (void)viewDidLoad {
.......
self.tableView.editing = YES; //set editing property of tableview
.......
}
editing - A Boolean value that determines whether the table view is in editing mode.
Declaration
#property(nonatomic, getter=isEditing) BOOL editing
Discussion
When the value of this property is YES, the table view is in editing
mode: The cells of the table might show an insertion or deletion
control on the left side of each cell and a reordering control on the
right side, depending on how the cell is configured. (See
UITableViewCell Class Reference for details.) Tapping a control causes
the table view to invoke the data source method
tableView:commitEditingStyle:forRowAtIndexPath:.
The default value is NO.
then return that cell in which you want to have an insert control
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleInsert;
}