UITableView: moving rows around in editing mode - iphone

-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
NSUInteger row = [indexPath row];
if(row == 0)
return NO;
return YES;
}
My problem is: although i specified NO for row 0. If I attempt to drag row 1 to replace row 0, the animation lets this happen anyways. The problem is that the above method only decides whether or not there is a move icon on the right of the row. Any ideas on how to stop row 0 from ever getting replaced?

Use
- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
from the UITableViewDelegate.
You can do something like this:
if ([proposedDestinationIndexPath row] > 0) {
return proposedDestinationIndexPath;
}
NSIndexPath *betterIndexPath = [NSIndexPath indexPathForRow:1 inSection:0];
return betterIndexPath;
(From "iPhone Programming" by Joe Conway & Aaron Hillegass)

Related

Expand only "one" cell at a time? Currently I can open multiple up

I followed Simon Lee's tutorial on animating uitableview height change, and it's working amazing! The only problem I seem to be facing is that I can tap several cells, and they all will remain expanded regardless if another is already expanded. I only want one expanded at any given time, and if another is already expanded, for it to be closed before a new one expands.
Simon mentioned that we should "store a single NSIndexPath as the current selected item instead on an array of indexes." I'm not exactly sure how to do this. If anyone could help out that would be awesome!
what you have to do is just add the code below in the tableView delegate method tableView:didSelectRowAtIndexPath: below the line
[selectedIndexes setObject:selectedIndex forKey:indexPath];
code to add
NSArray *key = selectedIndexes.allKeys;
for (int i = 0; i < [key count]; i++) {
if (![[key objectAtIndex:i] isEqual:indexPath]) {
[selectedIndexes setObject:[NSNumber numberWithBool:NO] forKey:[key objectAtIndex:i]];
}
}
Happy Coding :)
Enjoy the Day!!
Yes you can colapse the cells using the delegate methods of UITableView -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath and - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath :-
First declare the variable to get theindexPath
NSIndexPath *selectedCellIndexPath;
then get the cell's indexPath in didSelectRowAtIndexPath :-
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedCellIndexPath=indexPath;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationBottom];
}
Then do the collapsing and extending and set the lower and upper height:-
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(selectedCellIndexPath != nil
&& [selectedCellIndexPath compare:indexPath] == NSOrderedSame)
return yourCellsExtendedHeightInInteger;
return yourCellsCollapsedHeightInInteger;
}
or use
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(selectedCellIndexPath && indexPath.row == selectedCellIndexPath .row)
{
return 100;
}
return 44;
}
You can see this tutorial also:-

UITableView action row

On the WWDC 11 video 'session 125 - UITableCiew Changes, Tips, Tricks' at 24:30+ Mr Luke Hiesterman is giving a demo which adds a cell to the table view when a cell is selected.
I want to add that functionality to my IOS application, but I can't figure out how to make it.
Some code is not shown in the demo-video. And there is no downloadable source of the demo.
Can anyone help me out?
EDIT:
I can add a new row below the selected row but it is an other custom cell.
(I have a list of contracts which you can accept)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return ((indexPath.row >= [_contracts count]) ? 40 : 60);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_contracts count] + ((_addedRow) ? 1 : 0);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *reuseID = #"contract_cell";
ContractTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseID];
if(!cell)
{
// Load the top-level objects from the custom cell XIB.
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"ContractTableViewCell" owner:self options:nil];
// Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
cell = [topLevelObjects objectAtIndex:0];
[cell setBackgroundView:[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"contract_cell.png"]]];
}
NSLog(#"row?: %d", indexPath.row);
//I thought this would work... not.
if((indexPath.row >= [_contracts count]))
{
[cell.label setText:#"new"];
}
else
{
Contract *contract = [_contracts objectAtIndex:indexPath.row];
[cell.label setText:#"test"];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(!_addedRow)
{
_addedRow = YES;
[tableView deselectRowAtIndexPath:indexPath animated:NO];
[_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section]] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
return [NSIndexPath indexPathForRow:indexPath.row+1 inSection:indexPath.section];
}
For example I press the first row. A row is added below the first one. But now the cellForRowAtIndexPath.. is called for row 2 (needs to be the new custom)
how can I check if it is a new one?
You need to remember which of your rows is selected to show the added row at the correct index. Currently you're always assuming that the added row is the very last one because you're configuring it in tableView:cellForRowAtIndexPath: only if the index is bigger than your number of contracts.
Say you have five contracts and the third one is selected. if((indexPath.row >= [_contracts count])) is only true for the last row but actually want this condition to be true for the fourth row, so it should be if (indexPath.row == selectedRowIndex + 1) (you need to store the selected row index in some instance variable).
This answer should help you out. It tells you how to put your UITableViewController into "update mode" and then lets you insert new rows (even with animation): UITextField in UITableViewCell - adding new cells

Add "Swipe to delete" but not in all cells

I want to add in my tableview the possibility to "swipe to delete", but I don't want this for the last cell of the table!
I can check in - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath if the indexPath.row is the last one, but what I want is that if the user swipes on the last cell, nothing will appear (while in the others cells appears the text "delete").
I've tried this
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath{
if (indexPath.row != ([array count]-1)) {
return #"delete";
}
else {
NSString *a;
return a;
}
}
but of course it doesn't works (the app crashes).
I've tried with
return #"";
but the red button appears (with no text)!
What do you suggest me?
Thanks!
Try
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([indexPath row] == [tableView numberOfRowsInSection:[indexPath section]] - 1)
{
return UITableViewCellEditingStyleNone;
}
return UITableViewCellEditingStyleDelete;
}
The app crashes since you return an uninitialized pointer. But even then, you're doing it wrong ;-)
You want to implement tableView:editingStyleForRowAtIndexPath: and return UITableViewCellEditingStyleDelete for all cells, except for the last. You need to return UITableViewCellEditingStyleNone for the last cell to prevent that it can be deleted.

iPhone - How add a final line in a UITableView to add items, preventing rearranging of this line

I have a UITableView put in edited mode.
self.tableView.editing = YES;
In this table view, I have some lines displayed in a custom cell, and I'd like to add one at the end that would allow the user to add an item (using another view).
So I wrote some lines :
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return "number of lines" + 1;
}
- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.row+1 != [tableView numberOfRowsInSection:0]) {
return UITableViewCellEditingStyleDelete;
}
else {
return UITableViewCellEditingStyleInsert;
}
}
- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row + 1 == [tableView numberOfRowsInSection:0]) {
cell.textLabel.text = #"Add a line...";
}
else {
do the stuff in the custom cell
}
}
Doing this way, the UITableView allows to rearrange any line. I can move the first line after the "add line", and move the "add line" in first position.
How may I remove the arrange button on the "add line" cell and prevent other lines to go under this one ?
Or is there a better way to code this ?
Implement
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
return ([indexPath row] != INDEX_OF_SPECIAL_ROW)
}
and
- (NSIndexPath *)tableView:(UITableView *)tableView
targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath
toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath
{
if ([proposedDestinationIndexPath row] < NUMBER_OF_ROWS)
{
return proposedDestinationIndexPath;
}
NSIndexPath *otherPath = [NSIndexPath indexPathForRow:NUMBER_OF_ROWS-1 inSection:0];
return otherPath;
}
The much easier way would be to set a tableFooterView. You can place any UIView in there, so not only are you allowed to add a UITableViewCell but also a completely custom subclass. Doing it this way you will avoid all the unnecessary checks.
return UITableViewCellEditingStyleNone
For that row in - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
And check out – tableView:moveRowAtIndexPath:toIndexPath:

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;
}