INSERT a new UITableView row with Editable UITextfields - ios

I've been working on this for many days now and cannot seem to make it work. It may not even be possible but there's gotta be a way!
I want to insert a new uitableview row that contains two uitextfields that are editable after insertion. Everything I've read says you should update your data model array before the cell is added, meaning that the texfields need text in them and then added to the array BEFORE inserting the row. I tried to add a prompting text but once the cell is added, I can tap into the textfields, but I can't change what's already there (my prompting text). The textviews are unreponsive other than being able to tap into them. How to do this?
I am presenting a UIDatePicker to fill in the textfields for a single row that is created when the app first boots up. I want users to be able to add another row for when they read a book a second time (or more!). The textfields no longer update with the datePicker's values once the next row is added.
I've tried to use an alertview with textfields in it (I'm using URBAlertView) so users can add their dates before adding the new row, but I haven't been able to get it to go.
Please let me know if you need more code or if this is even possible. Thanks in advance for your help!
- (IBAction)addNewRowButtonTapped:(id)sender {
[self showDatePicker];
[self.tableView beginUpdates];
_cell.startDateTextfield.text = #"add start date";
_cell.finishDateTextfield.text = #"add finish date";
//these two lines give me errors and causes a crash
//[_book.startReadArray addObject:_cell.startDateTextfield.text];
//[_book.finishReadArray addObject:_cell.finishDateTextfield.text];
NSArray *paths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:[_book.startReadArray count]-1 inSection:1]];
[[self tableView] insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationTop];
_numberOfRows ++;
[self.tableView endUpdates];
}

This should be relatively simple, create a custom view with a UITextField, make sure it is properly registered with the tableView so you can dequeue it, and your UITextField should be useable.
I've setup a demo project for you that demonstrates this: https://github.com/ekscrypto/Swift-Tutorial-InsertTextfield

Related

UITableView deleteRowsAtIndexPaths not working

I'm not quite sure what's going on here but I'm running into a contact form that I'm working with. In my form I've added the ability to add an email address to a contact. As pictured below, once the user clicks on "add email", a row is added to the "Emails" section.
However after I click to delete the email, an extra cell appears underneath the add email button (pictured below).
There's a little red box from what appears to be the prior delete cell as though the table isn't reloaded. Here's my delete code:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if( editingStyle == UITableViewCellEditingStyleDelete )
{
// Update the data source
NSMutableArray *fields = (NSMutableArray *)self.fields[#(indexPath.section)];
[fields removeObjectAtIndex:indexPath.row];
NSMutableArray *values = (NSMutableArray *)self.values[#([self fieldTypeAtIndexPath:indexPath])];
[values removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationMiddle];
}
}
So why is that cell remaining afterwards? I can call [tableView reloadData] within the method above and it removes the excess cell but then it messes up the animation. Can you shed some clarity on what I'm doing wrong here?
This looks like a bug.
I just noticed that even Apple has this same bug in their Reminders app. It seems to be related to deleting rows from a UITableView with variable height items.
In Apple's Reminders app, when you delete something from a long list with variable height items, you will see the same exact visual artifact for a split second. Then the table jumps and the list looks correct. I am assuming Apple just reloads the entire table view a second after the item is removed in order to fix the visual glitch.
My recommendation is to report this as a bug to Apple. For now you can reload the entire table view like Apple presumably does.
Option 1: Before you call deleteRowsAtIndexPaths, you must make sure that numberOfRowsInSection will return the correct value (e.g. if you originally had 2 rows in that section, it should now return 1 row).
Option 2: If that isn't the problem, I would try a different animation type (e.g. UITableViewRowAnimationAutomatic), just to see if that has any effect.
Option 3: Ensure that cellForRowAtIndexPath is returning a valid cell with its contents being reset. Otherwise it may be reusing an invalid cell or displaying something that was there before.
Option 4: Since the heights are different, ensure that heightForRowAtIndexPath is returning the correct value. You probably need to call reloadRowsAtIndexPaths in order for the table view to know about the height difference.
I had the same problem and I guess it might be a bug in iOS7 where it doesn't repaint the cells correctly, I fixed it but not in an efficient way, just in the "cellForRowAtIndexPath" , always create a new cell, don't dequeue it.

UITableView stop updating UI without crashing after adding data

I have a problem that is driving me crazy.
I have an UITableView that is always in editing mode (it has to be).
The user can add new rows to it.
The navigationItem.leftbarButton of the tableViewController pushes a new controller just to do it, let's call it "newRowsVC".
Before the push the tableViewController set itself as the delegate of the newRowsVC, the protocol has only a method:
-(void) aNewRowHasBeenCreated
{
[self.tableView reloadData];
}
I start adding rows and everything works fine, each now row is immediately displayed in the tableViewController until the last new row will force the tableView to scroll because there won't be anymore screen real estate for it. Here, I have no idea why, the tableView, only it, is as frozen, with no scrollbar and doesn't respond to input. The app continues to run without a crash and I can even dismiss the tableViewController by tapping the navigationItem.rightbarButtonItem.
I can keep creating new rows, they are added to the array, the number of row in the tableView data source is computed correctly. But the table is like dead.
If I dismiss the tableViewController and then I come back to it, I see that all the rows previously created, also the ones not shown as soon as they were created are there!
I really do not have idea of how I can fix this.
The first thing I tried was to force the scroll after the reload of the table but it didn't fix it.
-(void) aNewRowHasBeenCreated
{
[self.tableView reloadData];
[self.tableView setScrollEnabled:YES];
}
I also tried forcing the tableView to scroll to the last row but it didn't fix it.
-(void) aNewRowHasBeenCreated
{
[self.tableView reloadData];
[self.tableView setScrollEnabled: YES];
[self.tableView scrollToRowAtIndexPosition: [self.tableView indexPathsForVisibleRows]last object] atScrollPosition: UITableViewScrollPositionBottom animated:YES];
}
I check the number of rows each time the table is reloaded. No error, the number is perfect and the new rows data are correctly in the array, also the data for the cells not shown.
I thought it could be because the tableView is always in editing mode so I tried setting it to NO but nothing changes, id din't fix the problem.
Two notes:
1)the tableView has to be the delegate of each one of it's custom cells. They have an UITextField and an UIStepper, handled by the tableViewController. I already tried to not set the tableViewController as the delegate of its custom cells but nothing changes so the problem is not this.
2) self.tableView.bounces = NO but this has nothing to do with the scrolling issue, the scroll is enabled.
Update: After more tests I found that if I remove the dequeue of the reusable custom cell everything works fine, so the problem should be about the reuse.
static NSString *CellIdentifier=#"MyCell"
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
Nicola
After hours I got it. As pointed out by Amit the problem was hidden in cellForRowAtIndexPath... It was hard to catch because it didn't happened all the times and the first times I enabled/disabled the cell reusing everything seemed the same so I did not link the problem to it. I got back on it after I tried about all the other options I had been able to think about.
The problem was in the reuse of the cells and the fact that the custom cell has the tableView as its delegate to handle its textView and the stepper without exposing them.
I got rid of all the delagion stuff and exposed the textView and the stepper as public properties of the view. By doing this I was able to set the tableViewController directly to be the delegate of the cell.textView and to add directly a target/action for the stepper.
Everything works flawslessly now.
Thanks to everyone who tried to help me!
Nicola

How can I get the value of a UITableViewCell that is not visible?

I have a table view cell that has many rows with a UITextView. In those UITextView the user can enter values.
The problem that I have is that if I want to get the value of a row when the row is hidden I get nil.
NSString *cellValue = ((UITextField *)[[[self.table cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]] contentView] viewWithTag:[self.table cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]].tag]).text;
I was thinking to save the value that the user enters in the UITextField, but if the user clicks a row that performs the calculate the delegate textFieldDidEndEditing is not being called.
I don't know how can I get this values in a secure way.
You should target the data source that is behind the UITableView. The UITableView is just a display mechanism and not the primary resource for getting to data. You can get the index for the cell based on the title, but then I would look tot he data source to get your actual data. Better yet I would just handle all operations against the data source itself if that is possible. When you build the table initially your data source is a list or array of some kind I am sure so you can just use it. You may need to create a variable on the view and retain the list/array there to make sure you have it and you should be all set.
Good Luck!
As mentioned, you would generally use the UITableView data source to access this.
That said, you are probably not seeing the data you expect because the you are using dequeued cells. If the number of cells is small, you could consider caching your own cells in an NSDictionary whose keys are some combination of the section and row, and accessing their view hierarchy via your own cache.
You can access rows that are not currently displayed by directly referring the tableView's dataSource with the wanted IndexPath.
You can achieve this by doing something like this (when using Swift 3):
UITableViewCell cell = self.tableView(self.tableView, cellForRowAt: indexPath)
What is happening most likely is your reusing cells. So when your cell with the textfield goes off screen it gets reused so you cannot access it anymore. What you will need to do is grab the value out the the textfield right after they enter it and store it somewhere. You can use – textField:shouldChangeCharactersInRange:replacementString: so that when ever the user enters a character into the textfield you can save the string.

Set editing style on a UITableViewCell without reloading the cell

I'm building a view controller that is very similar to the Contacts app ABPersonViewController in editing mode.
I have a UITableView filled with Custom UITableViewCells which contain a UITextField where the user can enter a phone number
I have a situation where if someone starts entering in a new phone number (in the last cell of the section), a new empty table view cell is added to the bottom of the table, and the editing style of the current table view cell is set to UITableViewCellEditingStyleDelete, like in the Contacts app in the same situation.
My problem is that I don't know how to change the editing style of the UITableViewCell without reloading it. The problem with reloading it is that its UITextField that is currently being edited will be released, dismissing the keyboard.
Is there a way to change the editing style of the UITableViewCell without reloading it? Or another way of accomplishing what I want?
Yes, just use the UITableViewCell method setEditing:animated:.
I was able to accomplish what I wanted by following the answer here:
https://stackoverflow.com/a/5664207/472344
I have successfully set editing cell with multiple selection applying below code:
[self.tableView setEditing:YES animated:YES];
self.tableView.allowsMultipleSelectionDuringEditing = YES;
// Add these two lines can fix icon issue.(First time enter edit mode still show delete icon.)
[self.tableView beginUpdates];
[self.tableView endUpdates];

Saving data from custom UITableViewCell

I have an edit screen which is comprised of one huge UITableView.
Each cell in the table is a custom UITableViewCell comprising of a UITextField
I am able to load up the table, scroll around and enter information. However, during scrolling the table cells lose the data entered and I understand why this happens.
I realize I need to save the data as soon as the user finishes entering the data using the textFieldDidEndEditing method.
Question: I want to store the data entered in a custom object that is available in the ViewController - how do I access this from the UITableViewCell? Also, how do I identify the row and position of the table cell so that based on the position I can populate a specific attribute in the object
Question: Am I doing this the right way? Or is there a better way to do this?
As you are mentioning only a single text field, we can make do with an NSMutableArray object containing strings for each cell. You will have to fill the array with empty strings and use this info to fill the text field. It will be something like,
textField.text = [textFieldStrings objectAtIndex:indexPath.row];
Since you're doing it programmatically within the view controller, you will have to set the view controller as your text view delegate. You can do
- (void)textFieldDidEndEditing:(UITextField *)textField {
UITableViewCell *cell = (UITableViewCell*)textField.superview;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
... Since you've the index path, you can map it to an array.
[textFieldStrings replaceObjectAtIndexPath:indexPath withObject:textField.text];
}
This would be the basic idea. Start with a mutable array of empty strings and modify them every time the text field is updated by replacing the original text with the new text. Hope this helps.

Resources