reload UITableViewCell only after Insert-Animation finished - iphone

so i'm inserting a UITableViewCell into my tableView.
[searchTableView beginUpdates];
[searchTableView insertRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationTop];
[searchTableView endUpdates];
after this i'm running an asynchronous request which updates the tableviewcell with a fade animation.
[someRequestWithCompletion:^(id data) {
dispatch_async(dispatch_get_main_queue(), ^{
[searchTableView beginUpdates];
[searchTableView reloadRowsAtIndexPaths:#[newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
[searchTableView endUpdates];
});
}];
but it is possible that this request finishes before the insert animation is finished. if the request finishes after the insert-animation, there is no problem, if it finishes before and calls a reloadRowsAtIndexPaths on the same cell that is currently inserting, the cell displays immediately and forces a reload while the insert-animation fades out.
is there a way to trigger the reload update after the cell is completely inserted? what would be the best way to do this?
if there are any questions, please let me know in the comments

Try This..
U can reload your TableView After Completion of Animation.
[CATransaction begin];
[searchTableView beginUpdates];
[CATransaction setCompletionBlock: ^{
[searchTableView reloadData];
}];
[searchTableView deleteRowsAtIndexPaths: [NSArray arrayWithObjects: indexPath, nil]
withRowAnimation: UITableViewRowAnimationAutomatic];
[searchTableView endUpdates];
[CATransaction commit];

Related

refresh the table in view controller by using refresh bar button

I want to add a refresh bar button in my view controller
I put in the viewdidload() this code:
UIBarButtonItem *refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:#selector(refreshTable)];
self.navigationItem.leftBarButtonItem = refreshButton;
and the refreshtable function is :
- (void) refreshTable
{
[self.tableView reloadData];
NSLog(#"table is refreshing ....");
}
this is a screenshot of the refresh button:
but it doesn't work, shall i add something else in the refreshtable function?, the table view doesn't refreshed!, the message "table is refreshing ...." appears everytime i click on the refresh button, but the table doesn't load new data!
when refreshing, can I have this icon somewhere?
If I had two table view in my view controller, and I want when I click on the refresh button, just the first table to be reloaded,
I put the same you code that you suggested, but it doesn't work here! should I do something else?
The issue is you added the data fetching logic in your viewDidLoad (according to your comments).
The viewDidLoad method will be called when the view is loading. It' won't be called unless you dismiss the view and present it again(it won't be calles if you call the reloadData it's just for reloading the UITableView).
When you call the reloadData the data source is still with old data, it is not updated.
So implement your refreshTable method like:
- (void) refreshTable
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://....../fastnews.php"]];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
}
-(void)fetchedData:(NSData *)responseData
{
NSError *error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
fastResults = [json objectForKey:#"nodes"];
[self.tableView reloadData];
}
try this .
[self.tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:[tableView indexPathsForVisibleRows]
withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
You need to update your dataSourceAaaay in refreshTable method before reload table.

scrollToRowAtIndexPath not scrolling to inactive/unloaded cells

I've noticed that scrollToRowAtIndexPath:atScrollPosition:animated: doesn't scroll to cell that are not currently in view, so If I have 100 cells and I need to get to the one at 70, the call to that selector will do nothing.
Is there a way I can get that cell into memory? I already have the cell's index path...
I need to scroll to that position in my app when the user would want to go there.
Thanks for any thoughts!
EDIT: #dasblinkenlight
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillShow) name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardWillHide) name:UIKeyboardWillHideNotification object:nil];
}
- (void)keyboardWillHide
{
//Load remote cell here then scroll
// :( dont know how to load remote cell yet
}
- (void)keyboardWillShow
{
//Load remote cell here then scroll
// :( dont know how to load remote cell yet
//_cellIndexPath gets assigned on didSelectRowAtIndexPath:
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_cellIndexPath.row inSection:_cellIndexPath.section] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
EDIT2:
- (void)keyboardWillShow
{
//Load remote cell here then scroll
[NSThread detachNewThreadSelector:#selector(keyboardWillShowThreaded) toTarget:self withObject:nil];
}
- (void)keyboardWillShowThreaded
{
[NSThread sleepForTimeInterval:2.0];
[self performSelectorOnMainThread:#selector(keyboardWillShowMainThread) withObject:nil waitUntilDone:YES];
}
- (void)keyboardWillShowMainThread
{
//Get the cell
//_textFieldThatHasFirstResponder is a subview in the cell
//This returns null, probably because the cell is not loaded into memory
UITableViewCell *cell = [_textFieldThatHasFirstResponder superview];
NSLog(#"CELL TO SCROLL TO: %#",cell);
NSIndexPath *indexPathForCell = [self.tableView indexPathForCell:cell];
[self.tableView scrollToRowAtIndexPath:indexPathForCell atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
OK, I've got the cause of this, see, you have:
NSIndexPath *indexPathForCell = [self.tableView indexPathForCell:cell]; // nil here
[self.tableView scrollToRowAtIndexPath:indexPathForCell atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
When you send indexPathForCell: for an out-of-view cell it returns nil, so tableView doesn't know where to scroll to.
you can implement a delegate so that you can call it from the class where you are in, so that is can update the position
"I've noticed that scrollToRowAtIndexPath:atScrollPosition:animated:
doesn't scroll to cell that are not currently in view, so If I have
100 cells and I need to get to the one at 70, the call to that
selector will do nothing. "
No. it is not true. I have just tried with a tableview with 100 rows. The following code works well.
[tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:78 inSection:0]
atScrollPosition:UITableViewScrollPositionNone animated:YES];
I don't think adding sleep is changing anything. It just delays execution but does not affect the order. Can you check if the index your are passing to scrollToRowAtIndexPath is valid? I remember seeing the same problem myself but it was related to invisible cell. It was impossible to retrieve invisible cell (tableView returned nil) and therefore its index path was nil and thus scrolling failed.
You could store locations of all cells or compute it on the fly and then pass it to
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated;
It's the only solution I can imagine.

Call a method after [self.tableView endUpdates] ended

I want to add my sliderButton after the animation has ended but this doesn't work, it adds the button while the deletion of the section is being animated.
[coursesTable beginUpdates];
[coursesTable deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationTop];
[coursesTable endUpdates];
[self.view addSubview:sliderButton];
Is there a possibility I can call a method that does that for me when the animation ended?
I believe that wrapping your table update in animateWithDuration would work:
[UIView animateWithDuration:0.0 animations:^{
[coursesTable beginUpdates];
…
[coursesTable endUpdates];
} completion:^(BOOL finished) {
// Code to run when table updates are complete.
}];
Other methods that I found suggested here on Stack Overflow did not work for me.
I used this technique at one time and tested it enough to verify that the completion block was called after I called the table's endUpdates method, but rewrote my code so I didn't need it any more before I had completely verified that the animation was actually finished.
You can find the answer here:
How to detect that animation has ended on UITableView beginUpdates/endUpdates?
Hope it helps!
I have also not found the solution of this.I have to use performSelector:withObject:afterDelay: for getting my work done.
[coursesTable deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationTop];
after this the tableview again asks data from its datasource... so all the datasource methods including cellforRow is again called..
i suggest keeping a BOOL is Deleting.
so now do this
[coursesTable beginUpdates];
[coursesTable deleteSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationTop];
[coursesTable endUpdates];
Deleting = YES;
in cellForRow method
{
if (index path.row == lastrow ) // last rowcell is reached
if(Deleting)
{
//tableView has reloaded.
[self.view addSubview:sliderButton];
Deleting = NO;;
}
}

How can I tell When a UITableView animation has finished?

How can I tell when [UITableView setEditing:YES animated:YES] has completed?
I don't want to give any context, because I want to avoid anybody giving me workarounds, which do not interest me.
What I want is to call the above, then have a separate function called when the animation is completed.
I am edited the post to give context and some workarounds.
Originally I setEditing and immediately reload the table data.
[tableView setEditing:YES animated:YES];
[tableView reloadData];
The problem is that the table reloads before the animation begins, and so the animation is never seen.
Here are some various workarounds:
[tableView setEditing:YES animated:YES];
[self performSelector:#selector(ReloadTable) withObject:nil afterDelay:1.0];
This works but if I get the delay incorrect then it will look bad. So I need to know what the delay is, which I can figure out, but we are not gauranteed that the delay will always be the same.
isEditing = YES;
[tableView reloadData];
[tableView setEditing:YES animated:YES];
This could work, but the table behaves differently depending on if we are in editing mode. So I have to use my own isEditing variable instead of the standard UITableView.editing. I would rather not have to create a new boolean isEditing variable.
[tableView setEditing:YES animated:YES];
[tableView insertRowsAtIndexPaths:path withRowAnimation:UITableViewRowAnimationTop];
This almost works well but in editing mode the first row should have the UITableViewCellEditingStyleInsert, while the other rows get UITableViewCellEditingStyleDelete. And with the above code the editing style gets set BEFORE the row is added. Therefore the second row ends up with UITableViewCellEditingStyleInsert.
[CATransaction begin];
[CATransaction setCompletionBlock: ^{
// your animation has finished
}];
[tableView setEditing:YES animated:YES];
[CATransaction commit];
Note that setCompletionBlock must be on the top.
In iOS 4 you can do the following:
[UIView animateWithDuration:0.3f
animations:^{
[self.tableView setEditing:YES animated:NO];
}
completion:^(BOOL finished){
// Do something
}
];
Swift 4 version of accepted answer:
CATransaction.begin()
CATransaction.setCompletionBlock {
// your animation has finished
}
tableView.setEditing(true, animated: true)
CATransaction.commit()

Scroll UITableView so that the header isn't visible

I've got a UITableView with a UISearchBar as the tableViews.tableHeaderView. Just like the new Mail.app, Notes.app, etc. in 3.0. I want to hide the SearchBar until the user drags it in his sight.
My attempt only works when there're a couple of items in the tableView, so that the tableView actually wants to scroll. I call this in loadView:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self._tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
Nevertheless it seems that Apple handles such a serachbar differently. After draging out the searchbar it doesn't seem to be bounded to the tablecells anymore (in Notes.app, not in Mail.app).
But perhaps Apple has a distinct method for that new 3.0 behaviour, and I just can't find it?
Maybe you can try it this way...
[self.tableView setContentOffset:CGPointMake(0,40)];
Worked for me too. I used the following:
[self.tableView setContentOffset:CGPointMake(0, self.searchDisplayController.searchBar.frame.size.height) animated:NO];
to query the height of the search bar.
This one gets you the exact same behavior as iPod.app:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
CGFloat searchBarHeight = CGRectGetHeight([[[self searchDisplayController] searchBar] frame]);
if ([[self tableView] contentOffset].y < searchBarHeight)
[[self tableView] setContentOffset:CGPointMake(0, searchBarHeight)];
}
This works for me.
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.bounces = YES;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.tableView setContentOffset:CGPointMake(0, 44)];
}
I had to scroll first to top then setContentOffset to 0, Then searchBar will be visible :
self.tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)
self.tableView.setContentOffset(CGPointMake(0, 0), animated: false)
I kind of like doing it this way:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Hide the table view header by default.
NSIndexPath *index = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView scrollToRowAtIndexPath:index atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
This way you don't really need to worry about how tall your header is. It just works!