I have a tableview. I need to add cells of the tableview at a particular interval of time after loading so that we can view loading the cells.How can i do this. Any idea will be greatly appreciated!
Set up a timer in your viewDidLoad method of your UITableViewController like so:
- (void)viewDidLoad {
[super viewDidLoad];
// Schedule a timer to fire every 5 seconds and call our
// insertNewObject method
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5
target:self
selector:#selector(insertNewObject)
userInfo:nil
repeats:YES];
}
Then insert a new row into our data source and tell the tableView about it
- (void)insertRow {
// dataSource defined elsewhere
[dataSource addObject:#"New row"];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:([dataSource count] - 1) inSection:0];
NSArray *indexPaths = [NSArray arrayWithObject:indexPath];
[[self tableView] beginUpdates];
[[self tableView] insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade];
[[self tableView] endUpdates];
}
Related
So, this issue follows on from a previous issue, but I decided to post a new question to keep things relevant and tidy.
Basically, when the following piece of code is called, there is no difference between UITableViewRowAnimationFade and UITableViewRowAnimationNone:
- (void) setEditing:(BOOL)editing animated:(BOOL)animated
{
[tvController.tableView beginUpdates];
if (editing == YES) {
[tvController.tableView deleteRowsAtIndexPaths:[settingsArray objectAtIndex:0] withRowAnimation:UITableViewRowAnimationFade];
}else {
UITableViewRowAnimation animation = animated ? UITableViewRowAnimationFade : UITableViewRowAnimationNone;
[tvController.tableView reloadRowsAtIndexPaths:[settingsArray objectAtIndex:0] withRowAnimation:animation];
[tvController.tableView reloadSectionIndexTitles];
self.navigationItem.hidesBackButton = editing;
}
[tvController.tableView endUpdates];
}
Greatly appreciate any help. It still enters editing mode, but does not animate into it, depsite YES being passed into animated.
EDIT: The animation works fine when I'm actually deleting things using the following code:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *stuff = [documentsPath stringByAppendingPathComponent:#"stuff.plist"];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:stuff];
if (fileExists) {
NSMutableDictionary *propertyList = [[NSMutableDictionary alloc] initWithContentsOfFile:enteredPlaces];
[propertyList removeObjectForKey:[[settingsArray objectAtIndex:1] objectAtIndex:indexPath.row]];
[propertyList writeToFile:stuff atomically:YES];
}
[[settingsArray objectAtIndex:1] removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
}
}
It just doesn't work when the user presses the edit button and the table goes into editing mode, the tableview just snaps statically into edit mode.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
[db DeleteData:[NSString stringWithFormat:#"%d",indexPath.row]];
[data removeObjectAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation: UITableViewRowAnimationFade];
}
}
I have same scenario in my application, I was facing the similar issue. I guess below function will solve your issue :
- (void) setEditing:(BOOL)editing animated:(BOOL)animated
{
[tvController.tableView setEditing:editing animated:animated]; //this LOC is added
[tvController.tableView beginUpdates];
if (editing == YES) {
[tvController.tableView deleteRowsAtIndexPaths:[settingsArray objectAtIndex:0] withRowAnimation:UITableViewRowAnimationFade];
} else {
UITableViewRowAnimation animation = animated ? UITableViewRowAnimationFade : UITableViewRowAnimationNone;
[tvController.tableView reloadRowsAtIndexPaths:[settingsArray objectAtIndex:0] withRowAnimation:animation];
[tvController.tableView reloadSectionIndexTitles];
self.navigationItem.hidesBackButton = editing;
}
[tvController.tableView endUpdates];
}
-(void)deleteRecord:(NSInteger)recordNo:(NSInteger)sectionNo:(BOOL)isEditMode:(BOOL)isAnimate {
if(isEditMode){
NSIndexPath *indexP=[NSIndexPath indexPathForRow:recordNo inSection:sectionNo];
[tvController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObjects:indexP, nil] withRowAnimation:UITableViewRowAnimationLeft];
}
else {
if(isAnimate)
[tvController.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexP, nil] withRowAnimation:UITableViewRowAnimationFade];
else
[tvController.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexP, nil] withRowAnimation:UITableViewRowAnimationNone];
[tvController.tableView reloadSectionIndexTitles];
self.navigationItem.hidesBackButton = editing;
}
[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(reload) userInfo:nil repeats:NO];
}
-(void)reload {
[table reloadData];
}
if the only thing that you are annoyed by is the fact that the delete doesn't fade, it is because you are anticipating something that the interface doesn't provide.
you mentioned that you know individual cells can be set to be non-editable, and this is the standard way to do this. the mechanism simply does not anticipate not using the UITableViewDataSource to provide the information about what to display (or, in your case, what is in the data being changed) simply by hitting the edit/done button.
by trying to combine the two, you are confusing the animation that is supposed to happen.
probably the best you can do is something like the following (and the animation duration and length could possibly be 0, since you're asking for separate animation in the tableView), which will cause your animation to occur after the animation that opens up editing mode.
- (void) setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated]; // possibly not necessary depending upon your class hierarchy
[tvController.tableView setEditing:editing animated:animated];
[UIView animateWithDuration:0.25 delay:0.05 options:UIViewAnimationCurveLinear
animations:^{
[tvController.tableView beginUpdates];
if (editing == YES) {
[tvController.tableView deleteRowsAtIndexPaths:[settingsArray objectAtIndex:0] withRowAnimation:UITableViewRowAnimationFade];
} else {
UITableViewRowAnimation animation = animated ? UITableViewRowAnimationFade : UITableViewRowAnimationNone;
[tvController.tableView reloadRowsAtIndexPaths:[settingsArray objectAtIndex:0] withRowAnimation:animation];
[tvController.tableView reloadSectionIndexTitles];
self.navigationItem.hidesBackButton = editing;
}
[tvController.tableView endUpdates];
}
completion:nil];
}
I can successfully push only next view in my iPhone app. However, cause the next view retrieves data to populate the UITableViews, sometimes waiting time could be a few seconds or slightly longer depending on connection.
During this time, the user may think the app has frozen etc. So, to counter this problem, I think implementing UIActivityIndicators would be a good way to let the user know that the app is working.
Can someone tell me where I could implement this?
Thanks.
pushDetailView Method
- (void)pushDetailView {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//load the clicked cell.
DetailsImageCell *cell = (DetailsImageCell *)[tableView cellForRowAtIndexPath:indexPath];
//init the controller.
AlertsDetailsView *controller = nil;
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
controller = [[AlertsDetailsView alloc] initWithNibName:#"DetailsView_iPad" bundle:nil];
} else {
controller = [[AlertsDetailsView alloc] initWithNibName:#"DetailsView" bundle:nil];
}
//set the ID and call JSON in the controller.
[controller setID:[cell getID]];
//show the view.
[self.navigationController pushViewController:controller animated:YES];
You can implement this in didSelectRowAtIndexPath: method itself.
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhiteLarge];
cell.accessoryView = spinner;
[spinner startAnimating];
[spinner release];
This will show the activity indicator at the right side of the cell which will make the user feel that something is loading.
Edit: If you set the accessoryView and write the loading code in the same method, the UI will get updated only when all the operations over. A workaround for this is to just set the activityIndicator in the didSelectRowAtIndexPath: method and call the view controller pushing code with some delay.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhiteLarge];
cell.accessoryView = spinner;
[spinner startAnimating];
[spinner release];
[self performSelector:#selector(pushDetailView:) withObject:tableView afterDelay:0.1];
}
- (void)pushDetailView:(UITableView *)tableView {
// Push the detail view here
}
I have a UITableView which lies in a NavigationController that is in a popover. So, I put an edit/done UIToolBarItem which works fine and you can see the delete accessory icon showing up. However, when I rearrange the items, the edit/done button stops working...
I debugged and it still gets called but it doesn't seem to update and is sort of preventing any further updates from other buttons. Is this possibly a problem with the simulator?
Thanks for the help in advance!
Edit: Adding code
addButton = [[UIBarButtonItem alloc] initWithTitle:#"Edit" style:UIBarButtonItemStyleBordered target:self action:#selector(editTable:)];
And later on the method:
- (IBAction) editTable: (id) sender
{
if ([[addButton title] isEqualToString:EDIT_STRING]) {
[addButton setTitle:DONE_STRING]; //enabling edit mode
[super setEditing:YES animated:YES];
[[self tableView] setEditing:YES animated:YES];
}
else {
[addButton setTitle:EDIT_STRING]; //done with editing
[[self tableView] setEditing:NO animated:YES];
[super setEditing:NO animated:YES];
[[self tableView] setNeedsDisplay];
[[self tableView] reloadData];
}
}
The second part is definitely called as the addButton text is changing. However, it stops working once I make an edit (such as re-arranging a row)
Alright,
I seemed to have called [tableView beginUpdates] and never called endUpdates, which was causing all updates to not be registered. If anyone else does that...
- (void)tableView:(UITableView *)tableview commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[self.tv deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tv reloadData];
}
}
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
[self.tv setEditing:editing animated:YES];
}
I have the following error. 'indexPath' undeclared (first use in this function).
Code.
didSelectRowAtIndexPath
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleWhiteLarge];
cell.accessoryView = spinner;
[spinner startAnimating];
[spinner release];
[self performSelector:#selector(pushDetailView:) withObject:tableView afterDelay:0.1];
}
pushDetailView
- (void)pushDetailView:(UITableView *)tableView {
// Push the detail view here
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//load the clicked cell.
DetailsImageCell *cell = (DetailsImageCell *)[tableView cellForRowAtIndexPath:indexPath];
//init the controller.
AlertsDetailsView *controller = nil;
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad){
controller = [[AlertsDetailsView alloc] initWithNibName:#"DetailsView_iPad" bundle:nil];
} else {
controller = [[AlertsDetailsView alloc] initWithNibName:#"DetailsView" bundle:nil];
}
//set the ID and call JSON in the controller.
[controller setID:[cell getID]];
//show the view.
[self.navigationController pushViewController:controller animated:YES];
}
I think it's because I'm not parsing the indexPath value from didSelectRowAtIndexPath to pushDetailView but I don't know how to approach this.
Could someone please advise?
Thanks.
The problem is that you pushDetailView: method has no indexPath variable on it's scope.
Instead of
- (void)pushDetailView:(UITableView *)tableView {
You should make your method like this:
- (void)pushDetailView:(UITableView *)tableView andIndexPath: (NSIndexPath*) indexPath {
and then indexPath would be declared on the method scope.
Then, inside your didSelectRowAtIndexPath method, replace
[self performSelector:#selector(pushDetailView:) withObject:tableView afterDelay:0.1];
for the code bellow:
double delayInSeconds = 0.1;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self pushDetailView: tableView andIndexPath: indexPath];
});
This uses GCD to execute the code after a delay, instead of the performSelector: withObject :afterDelay: and here is a nice post explaining why sometimes is better to opt for this aproach
Since you need to provide two arguments and perform after a delay package both arguments in an NSDictionary and pass it:
NSDictionary *arguments = [NSDictionary dictionaryWithObjectsAndKeys:
tableView, #"tableView", indexPath, #"indexPath", nil];
[self performSelector:#selector(pushDetailView:) withObject:arguments afterDelay:0.1];
...
- (void)pushDetailView:(NSDictionary *)arguments {
UITableView *tableView = [arguments objectForKey:#"tableView"];
NSIndexPath *indexPath = [arguments objectForKey:#"indexPath"];
...
Or as #Felipe suggests, use GCD.
I need to add a new row in the tableview when i select a row in the table.
Am i suppose to use the following method???
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
What shoud i write inside the method to add a new row.?
Please do help me,
Thanks in advance
Shibin.
When I add a row dynamically, I use insertRowsAtIndexPaths: here is an example function
- (void) allServersFound {
// called from delegate when bonjourservices has listings of machines:
bonjourMachines = [bonjourService foundServers]; // NSArray of machine names;
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
int i = 0;
for (NSArray *count in bonjourMachines) {
[tempArray addObject:[NSIndexPath indexPathForRow:i++ inSection:0]];
}
[[self tableView] beginUpdates];
[[self tableView] insertRowsAtIndexPaths:(NSArray *)tempArray withRowAnimation:UITableViewRowAnimationNone];
[[self tableView] endUpdates];
[tempArray release];
}
If you are filling your table from an array, you can just add an element to your array and call [myTable reloadData]
reload data is nice and simple, but does not animate as far as I can tell...
ReloadData can add row in teble view but if you want some animation when row add then you should use these lines of code.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
[tempArray addObject:[NSIndexPath indexPathForRow:indexPath.row+1 inSection:0]];
[mainArray insertObject:#"new row" atIndex:indexPath.row+1];
[[self tableView] beginUpdates];
[[self tableView] insertRowsAtIndexPaths:(NSArray *)tempArray withRowAnimation:UITableViewRowAnimationFade];
[[self tableView] endUpdates];
}