Adding new cells in a particular position in UITableView - ios

I just started learning iOS programming and have a requirement where the user is able to edit their profile (adding emails, numbers , etc). I wanted to do it like how the native Contacts app of iOS7+ does for adding new contacts. You see a cell saying "add phone" and when you tap it you get a new cell just above it. as you tap the "add phone" cell you get new cells piling up above. Can anyone point me in the right direction to achieve similar results. Thanks for any help in advance.

There's a few different ways to insert a cell into a table view. You can add cells in an animated fashion using the table's insertion style in the table view's editing modes.
It may be easier to just add an entry to your data source array at the desired index and call [tableView reloadData]
For example if the add phone button was the last cell in the table:
in didSelectRowAtIndexPath:
if (indexPath.row == mutableArray.count - 1) {
// Clicked add phone button
[mutableArray addObject:/*phone cell placeholder*/ atIndex:mutableArray.count - 1];
tableView reloadData];
}
After your table view reloads, there will be a new cell above the last cell where you can add a text field to enter a phone number.

You need to add that cell's data in your data source and call reload ...a simple one.
for showing delete or insert button you need to use
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (<condition>) {
return UITableViewCellEditingStyleInsert;
}
else {
return UITableViewCellEditingStyleDelete;
}
return UITableViewCellEditingStyleNone;
}
for action specific to rows you can use following methods
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation NS_AVAILABLE_IOS(3_0);
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath NS_AVAILABLE_IOS(5_0);

Related

Reorder NSArray based on UITableView Edit

I have an app filled with over 500 songs for churches. I am adding a new feature, which will allow them to create a "slide show" of manually chosen songs. The way the layout is now is that they have a UITableView of all the songs, and tapping a song adds that PPT's file location to an NSArray, and adds a checkmark to the row. Following this, they can click the Preview button, which will give them a tableview of just the songs they have chosen, in the order they chose it.
I would like to allow editing mode to delete songs, or reorder them, but am not sure how to have that also reorder the NSArray, so that item 0 will be the first song they have on the tableview, not just the first one they tapped.
I assume you have an array of songs, called songsArray which is used to populate table view. Then when reordering happens, add this method.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
NSObject *songToMove = [self.songsArray objectAtIndex:sourceIndexPath.row];
[self.songsArray removeObjectAtIndex:sourceIndexPath.row];
[self.songsArray insertObject:stringToMove atIndex:destinationIndexPath.row];
}
Dont forget to implement canMoveForRowAtIndexPath
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
This lets user to reorder all rows in your table view, if you don't want to let user to reorder specific rows, add check for indexPath.row and return NO for that row.

How to Save UITableViewController cells after rearrange to NSUserDefaults

I've an iOS application developed using Objective-C & Xcode 6.4
Right know I'm working on manually rearranging the UITableViewController cells, everything working great. But after I press the bar button EDIT and the "3 underlines" appear to drag the cell anywhere I want in the UITableViewController, (( I can't save what I did )). So how could I do a Persistent save the changes done to the table cells location ?? I mean, How to save The new rearranged NSMutableArray to a Property list -NSUserDefaults-.
I'm using a Mutable Array to display the table's cells and these methods below:
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleNone;
}
-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
FileWML *fileWML = [self.filesWML objectAtIndex:sourceIndexPath.row];
[self.filesWML removeObjectAtIndex:sourceIndexPath.row];
[self.filesWML insertObject:fileWML atIndex:destinationIndexPath.row];
}
PLUS would someone tell me how I can make the edit button display the word Done, while editing ?
(( I can't save what I did )) means: after I do the rearranging order I want, and then I go to the home view then I get back to the table view I edit and rearrange, the rearranging order I did get back to the default order. So, all the rearranging I did is gone.
Thanks and every help is appreciated.
You have to load your datas from your file in an array. This array will be your tableView dataSource.
When a row is moved, in the delegate method you have to change the object's place in the array. Your array should always be the same as your tableview !
Once the editing of the tableview is done you save the array in the file. The previous content in file should be erased. Then you reload your datas ([tableView reloadData].

Remove Indexpaths of UITableView that are off screen - indexPath.row is greater than the size of my array

Is it possible to remove indexpaths of UITableView that are off screen? My case calls for it, and I can't seem to find any methods or answers to remove an IndexPath.row that is off screen. I dont need to remove a cell, but only the indexPaths that are visible cells should remain.
Does this make sense?
Sorry I can't provide any code because I dont know where to start.
in iOS6, a new UITableViewDelegate function was introduced that does just this:
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath;
You can use this something like this:
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([tableView.indexPathsForVisibleRows indexOfObject:indexPath] == NSNotFound)
{
// This indeed is an indexPath no longer visible
// Do something to this non-visible cell...
}
}
If you are using UITableView properly and dequeueing cells using the method...
[tableView dequeueCellWithReuseIdentifier:#"blah" indexPath:indexPath];
then this will already be happening.
The tableview manages which cells are on screen. If you have 1000 rows in your table but only 10 rows on screen at a time then you will only ever have 10 cell objects in memory as those ten are reused for the rows that come onto the screen when others are scrolled off the screen.

Change UITableView delete button text after it's first shown (in editing mode)

I would like to change the delete text displayed by a UITableView once editing mode has begun.
The delegate method:
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
is called only when the deleteButton at index path is displayed for the first time, but if my model changes beneath it I need to update this text. Is it possible to cause this method to be called again without reloading the entire section? See code below, and thank you for your help in advance.
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath {
ContainerTableViewCell *cell = (ContainerTableViewCell*)[self.tableView cellForRowAtIndexPath:indexPath];
if ([cell.editPhotos count] > 0) {
return [NSString stringWithFormat:#"Delete %d photos", [cell.editPhotos count]];
}
else{
return #"Delete Section";
}
}
For a bit of context I have a UICollectionView nested within a UITableViewCell, a notification is sent when a cell is selected. I have tried reloading the section with:
[self.tableView reloadRowsAtIndexPaths:#[[self.tableView indexPathForCell:cell]] withRowAnimation:UITableViewRowAnimationNone];
but this is undesirerable because it causes a jump in the tableview and does not display the selection correctly. I have also tried:
[self.tableView.delegate tableView:self.tableView titleForDeleteConfirmationButtonForRowAtIndexPath:[self.tableView indexPathForCell:cell]];
in desperation. While this does cause the correct method to be called it does not change the delete text.
I just wrote a rudimentary test app where it works as expected.
I think maybe the way you get your data is not the best approach. You are querying a cell that is presumably dequeued and thus might not contain the most up-to-date information.
Instead, you should strive to achieve a true MVC pattern where your data is independent from your views, including collection view cells.
I found a solution to this problem although it is a bit of a hack. The delegate method
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
is called once for each cell every time the UITableView enters edit mode. Therefore in order to have the title change when the data changes I toggled the edit mode, using a bool to indicate that I wished to save selected information ie:
cell.retainEditSelection = YES;
[self.tableView setEditing:NO animated:NO];
[self.tableView setEditing:YES animated:NO];
cell.retainEditSelection = NO;
I use this every time something is selected that should change my delete text. Hope this helps .

how prevent cell width from expanding when done editing in UITableView

If you notice in the iPad contacts app when you tap on the "+" edit icon to add a new address, the cell does not expand to the left when the icon disappears (see 1st screen capture below). I am trying to do something similar but am having no luck (see 2nd screen capture below). I tried using the solution suggested here but didn't work.
For the record I am not trying to replicate the actual contacts app or integrate with contacts. We're working on something unrelated and my designer just wants to follow this pattern so doing some POC work to see what I can do.
EDIT - Another question, to verify my thinking, is that in the contacts app here what's happening is when the "+" icon is tapped, this cell is set to not editing any more, these text fields are added to the cell, the label changed, and then reloadData is called, right?
EDIT - Thanks for the suggestion Tim, I'm almost there! I had actually worked out 1/2 of it but then was running into the issue that the "+" icon was not animating out, just abruptly disappearing. So I added the reloadRows... call to commitEditingStyle but now the entire cell disappears. Here is my code:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCellEditingStyle editingStyle = UITableViewCellEditingStyleNone;
if (self.showEditingIcon) {
editingStyle = UITableViewCellEditingStyleInsert;
self.showEditingIcon = NO;
}
return editingStyle;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
// tableView.editing = NO;
// tableView.editing = YES;
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
You can see from my commented-out lines what I had originally, which was working but as I said, was not animating - the "+" button was just abruptly disappearing. If I tried to call [tableView setEditing:NO animated:YES] it would not work. When I tried to put these calls within a UIView animation block it did not look good - could see cell temporarily shift to left and back. However now with the reload call the cell disappears completely. At the moment I'm not making any changes to my cell (for example I'm not adding text fields, etc...) because I just want to make the "+" button animating out without making the cell expand the left work on the first iteration.
Any help greatly appreciated. Thanks.
The trick is to always be in editing mode such that the cell indention remains constant. When the cell is loaded, you decide what state it is in and return the appropriate editing style, either the "insert" style or "none":
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
BOOL shouldShowInsertButton = ...;//check internal state for cell
return shouldShowInsertButton ? UITableViewCellEditingStyleInsert : UITableViewCellEditingStyleNone;
}
Then when the edit button is tapped, you update your internal state and reload the cell:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
...;//update internal state for cell
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
EDIT
Here's a working sample project.

Resources