Deleting A TableView Cell Populated By A Dictionary - objective-c

So, deleting a table view cell is pretty straight forward. You remove the object from the array, and reload the table views data. Now... I have a table view that is populated from an NSMutableDictionary rather than array (we need to because we are parsing data and storing it). Does anyone know how to delete a cell via this method? I've tried simply calling [tableView reloadData]; which surprisingly worked, but obviously created an endless loop. I do not like endless loops. Does anyone have any suggestions? I couldn't find any relevant information in my research.
This is some relevant code in the cellForRowAtIndexPath delegate:
if (![frString isEqual:#"COOL STUFF"] && [subString isEqualToString:#"COOL"]) {
NSLog(#"A COOL is showing where it shouldn't, and it's %#", subString);
//remove all cells starting with COOL
[myDictionary removeObjectForKey:[self.dictName objectAtIndex:row]];
[tableView reloadData];
}
Also, I'd rather hide the cell instead of permanently removing the info from the dictionary. Is this possible?

There are a few things you're doing wrong :
You shouldn't remove the rows in the cellForRowAtIndexPath, because that's called when the tableview wants to render. So you remove a row, and then it renders again, calls cellForRowAtIndexPath:,...
Either delete the rows, or reload the data. deleteRowsAtIndexPaths: deletes sets of rows with animation, reloadData reloads the whole tableView, so it cancels the animation, and deleteRowsAtIndexPaths: becomes pointlesss
Ensure that you return the proper numberOfRowsInSection: did you remove the data from your dictionary ?

Related

OSX NSTableView insertRowAtIndexes

i already checked Using NSTableView insertRowsAtIndexes solution but It does not solve my problem.
i want insert row in nstableview at particular index(add dynamically)
index Set is valid, still it causes Program crash
NSIndexSet *indexSet=[NSIndexSet indexSetWithIndex:i];
[myTableView insertRowsAtIndexes:indexSet withAnimation:NSTableViewAnimationEffectFade];
1)is any thing wrong in my code?
2)Is there any another way to add row at particular index(add dynamically)?
The code is correct but first you have to insert the model object in the data source array to keep model and view in sync.
I got My mistake..... problem In other Code so This code is fine
But I want to add some points About insertRowsAtIndexes: method
Hope it will helps to other people
1)Dont called reloadData() because you are adding particular number of rows so calling reloadData() will reload all data and it will causes crash
2) Calling this method multiple times within the same beginUpdates and endUpdates block is allowed, and changes are processed incrementally
3)Most Important thing is indexSet must be within range
if you are Enter valid indexSet then The numberOfRows in the table view is automatically increased by the count of indexes.
4)you can select animation according to your need
Sample Code :
[yourTableView beginUpdates];
NSIndexSet* theIndexSet = [NSIndexSet indexSetWithIndex:[self.yourTableContents count]-1];
[yourTableView insertRowsAtIndexes:theIndexSet withAnimation:NSTableViewAnimationEffectFade];
[yourTableView endUpdates];

Using for loop to add to an array, objects aren't being added until for loop is completely done looping

So, I am using the code below to cycle through json arrays if the json array returns positive, I add the json array to another array containing all of total arrays. This can take some time to complete however, because it is going through several different json urls plugging in different dynamic information.
I edited the code below to show more basic information. My problem is that data isnt actually entered into the array until the for loop is finished. I need it to be added as soon as it finds it, not after it runs through all the onlineChannels.count - Any help?
for (int i = 0; i<onlineChannels.count; i++) {
[jsonResults addObject:[parsingJson objectAtIndex:0]];
NSLog(#"%i",jsonResults.count);
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
[self.tableView reloadData];
} else {
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
[self.tableView reloadData];
}
}
My problem is that data isnt actually entered into the array until the for loop is finished.
How do you know?
I see that you're calling -reloadData, so perhaps you're expecting the change to be reflected in your table immediately? I believe that your objects are being added to the array immediately, but the change isn't displayed on the table right away. -reloadData doesn't actually redraw the table -- drawing happens as part of the main run loop. You're probably running this code on the main thread, and by doing so you're preventing the run loop from getting any time to redraw the table.
The best way to solve this sort of problem is to move time consuming processes like your loop to background threads. That will leave the main thread free to redraw the table as necessary, and it'll make your app more responsive as well.
Also, making the table reload all its data every time you add a single item even when that item's row isn't being displayed seems quite wasteful. Consider instead calling a method like reloadRowsAtIndexPaths:withRowAnimation: to update only the part of the table that has changed.

How do you correctly get a row value from a table view?

I have implemented code that returns 0 every time. I'm trying to remove a string from a mutable array with the row value selected after hitting a button.
Related code:
- (IBAction)remove:(id)sender {
NSIndexPath *selectedIndexPath = [_tableView2 indexPathForSelectedRow];
[names removeObject:[NSString stringWithFormat:#"%i",selectedIndexPath.row]];
testLabel.text=[NSString stringWithFormat:#"%i",selectedIndexPath.row];
[_tableView2 reloadData];
}
The test label shows 0 every time the button is pressed, no matter where the tableview is selected.
Let me know if there is other relevant code (like the tableView) that you want me to post.
For a UITableView, the selected row concept is only valid while the user is touching the row. For that reason indexPathForSelectedRow is documented as returning “An index path identifying the row and section indexes of the selected row or nil if the index path is invalid.”
My opinion is that you are obtaining a nil result, and later calling the row method in that nil results in the zero that your are seeing as your name.
The solution depends on the rest of your data source implementation, but probably will involve storing the tapped index in didSelectRowAtIndexPath: to later use it in your remove method.
(I supposed that you are not using the allowsMultipleSelectionDuringEditing option nor are you editing the table).
Instead of:
[names removeObject:[NSString stringWithFormat:#"%i",selectedIndexPath.row]];
Try:
[name removeObjectAtIndex:selectedIndexPath.row];
Also, instead of using #"%i", try using #"%d" as mentioned here
Hope this helps!

How do I move data from one array to another? Using UITableView

Following on from my last question, I have a tableview populated with 2 NSMutableArrays, 2 sections and 2 section headers, all of which are working ok.
incompleteItems = [NSMutableArray arrayWithObjects: #"Item 1", #"Item 2", #"Item 3"... , nil];
completeItems = [NSMutableArray arrayWithObjects: nil];
I have set it up so that when a user swipes to the right, the cell accessory changes from disclosure to checkmark, and when swiping to the left changes from checkmark to disclosure. The gestures are achieved using handleSwipeLeft/Right methods.
What I'd like to do is when a user swipes to the right, the Item from the incompleteItems array is moved into the completedItems array, thus moving the item from the "Incomplete" section to the "Complete" section.
What would be the best way to achieve such a thing? My experience is limited so this is somewhat outside of my knowledge.
Any help appreciated as always.
You can simply add the object from one array to the other and then delete it immediately.
-(void)incompleteToCompleteAtIndex:(NSUInteger)index
{
[complete addObject:[incompleteItems objectAtIndex:index]];
[incompleteItems removeObjectAtIndex:index];
}
-(void)completeToIncompleteAtIndex:(NSUInteger)index
{
[incompleteItems addObject:[complete objectAtIndex:index]];
[complete removeObjectAtIndex:index];
}
Since this will only occur as a result of a UI action I assume that this will happen on the main thread and there will be no reason for locking.
Do you need a nice animation? Check out: deleteRowsAtIndexPaths:withRowAnimation: and insertRowsAtIndexPaths:withRowAnimation:. Make sure you wrap these calls with beginUpdates and endUpdates.
I don't know of an easy way to animate moving a cell from one tableView to another. I can imagine a method that requires you to do a 3-step animation manually, removing the cell (and adding it to a new view that floats in front of your whole view hierarchy), moving the new view to the destination location (you may need to do your own math to determine the screen coordinates of the destination) and finally an addition animation. You could use a dummy cell that is completely transparent and swap it in / out in the first and last pieces of the animation to give the tableview something to hold the place of your transferred cell.

How do I efficiently update a UITableView with Animation?

My iPad app features a UITableView populated from a feed. Like most RSS readers, it displays a list of links to blog posts in reverse chronological order, with their titles and a summary of each post. The feed updates frequently, and is quite large, around 500 posts. I'm using libxml2 push parsing to efficiently download and parse the feed in an NSOperation subclass, constructing entry objects and updating a database as I go. But then I need to update the UITableView with changes.
So far, the app has been updating the UITableView for every post parsed, as it is parsed. The parser performs a selector on the main thread to do this work. But this leads to some serious lag for a couple of seconds if a lot of cells need to be updated. I can mitigate this by running the table update on a background thread, but it seems that this is not a good idea. So now I'm trying to figure out how to update the table more efficiently on the main thread.
I could just call reloadData when all the posts have been parsed, but it's not very user friendly: there's no animation to indicate that anything has changed, just a flash and the new data is there. I'd much rather have it animate to show that new posts are added and old posts removed. Existing posts that are not removed from the feed should be pushed down the table by the new posts appearing at the top.
I know this is possible. Byline, to give one example, does a beautiful job. Each post is added or removed from the UITableView one-at-a-time with no gaps showing the table background. All without making the UI in the least bit unresponsive. How is that done??
My latest attempt is to update the table only after all the posts have been parsed (the parser is quite fast, so it's not much of a delay). It then loads the existing posts in an NSDictionary mapping their IDs to their indexes in the array used as the table data source. It then iterates over every object in the newly-parsed array of posts, adding NSIndexPath for each to arrays that are later passed to -insertRowsAtIndexPaths:withRowAnimation:, -deleteRowsAtIndexPaths:withRowAnimation:, and -reloadRowsAtIndexPaths:withRowAnimation: as appropriate to insert, remove, move, or update cells. For 500 posts, this takes around 4 seconds to update, with the UI completely unresponsive. That time is used almost exclusively for the UITableView animated updates; iterating over the two arrays of posts takes very little time.
I then modified it so that those are updated without animation, and I have separate arrays to insert/delete/reload with animation only for row positions corresponding to currently-visible rows. This is better, but gaps appear as posts are removed and new ones added.
Sorry this is so long-winded, but here's the upshot:
How can I update a UITableView, with new cells pushed on, others pushed off, and still others moved from one position to another, with up to 500 cells in the UITableView (6-8 are visible at one time), and each animation happening in sequence, all while the UI remains completely responsive?
This question actually has three answers. That is, there are three parts to this question:
How to keep the UI responsive
How to keep the updating fast
How to make table update animation smooth
UI Responsiveness
To solve the first problem, I now make sure that no more than one table-updating message can be delivered on each iteration of the main event loop. That prevents the main thread from locking up if the background thread is feeding it stuff to do faster than it can cope with it.
This is done thanks to example code sent to me by Byline author Milo Bird, which I then integrated into Dave Dribin's DDInvocationGrabber. This interface makes it super easy to queue a method to be invoked on the next available iteration of the main event loop:
[[(id)delegate queueOnMainThread]
parserParsedEntries:parsedEntries
inPortal:parsedPortal];
I quite like how easy it is to use this method. The parser now uses it to call all of the delegate methods, most of which update the UI. I've released this code on GitHub.
Performance
As for performance, I was originally updating one UITableView row at a time. This was effective, but somewhat inefficient. I went back and studied the XMLPerformance example, where I noticed that the parser was waiting until it had collected 10 items before dispatching to the main thread to update the table. This was key to keeping the performance up without making the UI lock up by updating all 500 rows at once. I played around with updating 1, 10, and all 500 rows in a single call, and updating 10 seemed to offer the best tradeoff between performance and UI lockup. five would probably work pretty well, too.
Animation
And finally, there's the animation. Watching the “Mastering Table Views” WWDC 2010 session, I realized that my use of the deleteRowsAtIndexPaths:withRowAnimation: and updateRowsAtIndexPaths:withRowAnimation: methods was wrong. I had been keeping track of where things should be added and removed in the table and adjusting the indexes as appropriate, but it turns out that's not necessary. Inside a table update block, one only needs to reference the index of a row from before the update, regardless of how many may be inserted or deleted to change its position. The update block, apparently, does all that bookkeeping for you. (Go to about the 8:45 mark in the video for the key example).
Thus, the delegate method that updates the table for the number of entries passed to it by the parser (currently 10-at-a-time) now explicitly tracks the positions of rows to be updated or deleted from before the update block, like so:
NSMutableDictionary *oldIndexFor = [NSMutableDictionary dictionaryWithCapacity:posts.count];
int i = 0;
for (PostModel *e in posts) {
[oldIndexFor setObject:[NSNumber numberWithInt:i++] forKey:e.ident];
}
NSMutableArray *insertPaths = [NSMutableArray array];
NSMutableArray *deletePaths = [NSMutableArray array];
NSMutableArray *reloadPaths = [NSMutableArray array];
BOOL modified = NO;
for (PostModel *entry in entries) {
NSNumber *num = [oldIndexFor objectForKey:entry.ident];
NSIndexPath *path = [NSIndexPath indexPathForRow:currentPostIndex inSection:0];
if (num == nil) {
modified = YES;
[insertPaths addObject:path];
[posts insertObject:entry atIndex:currentPostIndex];
} else {
// Find its current position in the array.
NSUInteger foundAt = [posts indexOfObject:entry];
if (foundAt == currentPostIndex) {
// Reload it if it has changed.
if (entry.savedState != PostModelSavedStateUnmodified) {
modified = YES;
[posts replaceObjectAtIndex:foundAt withObject:entry];
[reloadPaths addObject:[NSIndexPath indexPathForRow:num.intValue inSection:0]];
}
} else {
// Move it.
modified = YES;
[posts removeObjectAtIndex:foundAt];
[posts insertObject:entry atIndex:currentPostIndex];
[insertPaths addObject:path];
[deletePaths addObject:[NSIndexPath indexPathForRow:num.intValue inSection:0]];
}
}
currentPostIndex++;
}
if (modified) {
[tableView beginUpdates];
[tableView insertRowsAtIndexPaths:insertPaths withRowAnimation:UITableViewRowAnimationTop];
[tableView deleteRowsAtIndexPaths:deletePaths withRowAnimation:UITableViewRowAnimationBottom];
[tableView reloadRowsAtIndexPaths:reloadPaths withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
Comments welcome. It's entirely possible that there are more efficient ways to do this (the use of -[NSArray indexOfObject:] is particularly suspicious to me), and that I may have missed some other subtlety.
But even so, this is a huge improvement for my app. The UI now stays (mostly) responsive during a sync, the sync is fast, and the table update animation looks just about right.
Have you tried [tableView beginUpdates]; and [tableView endUpdate];?