The problem is the UI appears and then gets updated : giving a flickering affect.
I want the UI to be updated only once when user enters app, thus i've put reload in ViewDidLoad.. Here is the code .. Any help how can remove this flickering ... Some code example would help.
- (void)viewDidLoad {
[super viewDidLoad];
self.myTableView.dataSource = self;
self.myTableView.delegate = self;
PFQuery * getCollectionInfo = [PFQuery queryWithClassName:#"Collection"]; // make query
[getCollectionInfo orderByDescending:#"updatedAt"];
[getCollectionInfo setCachePolicy:kPFCachePolicyCacheThenNetwork];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[getCollectionInfo findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
CollectionQueryResult = (NSMutableArray *)objects;
[self.tableView reloadData];
// whenevr get result
}
else{
//no errors
}
}];
});
Why don't you simply call reloadSections method instead of [self.tableView reloadData];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationNone];
Refer cellForRowAtIndexPath delegate method. You might be doing some operation that might cause a flick. In my case, I was setting an image of an image view with animation. So whenever I reload the table, it was flickering due to that animation.
I removed that animation and worked for me..
Hope the below lines can help you in delaying and flickering issues
dispatch_async(dispatch_get_main_queue()
, ^{
[self.tableView reloadData];
});
Your download is asynchronous, so it won't complete before the view is shown. So, whatever you have in CollectionQueryResult will get displayed.
You could either:
Clear CollectionQueryResult so the table isn't populated till you get an update
Compare CollectionQueryResult and objects, then update only the visible cells where the data has changed (before setting CollectionQueryResult)
Note that for option 2 you will also need to compare the count of CollectionQueryResult and objects and then insert / delete rows from the table view as appropriate. The whole point of option 2 is to not call reloadData, so you need to do a lot more work...
You can also avoid calling reloadRowsAtIndexPaths if you get the visible cells and update them directly (which avoids any animation from happening). See cellForRowAtIndexPath:.
Related
I wonder is it possible to detect when UICollectionView after call realodData complete measuring contentSize.
It's I need to make scrollToItemAtIndexpath or more things that depends on contentSize?
I try solutions on Stack, but they not any time work fine.
I found pretty (i think) solution:
- (void)reloadDataCV {
__weak typeof(self) wSelf = self;
[wSelf.cv reloadData];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"HeightGCD:%#", #(wSelf.cv.contentSize.height));
[wSelf.cv scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:50 inSection:0] atScrollPosition:UICollectionViewScrollPositionBottom animated:YES];
});
Here i made example of how it's works
https://github.com/NelepovDmitry/iOSCommonMistakes/tree/master/iOS/CollectionView/TestCollectionGCD
I am new in iOS.
I am trying to implement upload data in uitableview one by one row.
for that i am using background task.
using following code.
-(void)MethodUploadBgTaskAssign
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *responseString = [self MethodlblUploadClicked];
dispatch_async(dispatch_get_main_queue(), ^(void)
{
if([responseString isEqualToString:#"UploadSuccess"])
{
[self ReloadTblData];
[self ContinueUploadData];
}
});
});
}
-(void) ReloadTblData
{
if([dataArr count]>0)
[dataArr removeObjectAtIndex:0];
[uitblData reloadData];
}
-(void) ContinueUploadData
{
if((dataArr count]>0)
[self MethodUploadBgTaskAssign];
}
My problem is uploading data in table after some time table reload with empty data
because all data uploaded at that time.
I want show updated ui after uploading each cell in table.
What will be necessary changes in code?
appreciate for help.
It looks to me as if you are updating the table - but in the wrong thread (hence the table never actually appears to update). You need to use performSelectorOnMainThread to update the UI.
[self performSelectorOnMainThread:#selector(ReloadTblData:) // Update the table on the main thread
withObject:nil
waitUntilDone:NO];
I think this should work - give it a go!
I am developing iPhone app in which i am downloading image from server in background,
Here is view of my application,
when i click on Button 1 i am fetching 5 data from server also images, after fetching data when user scrolls up i am fetching new 5 data from server again when user scrolls up i am fetching new 5 data from server and so on.
while fetching data for Button 1 if i click on Button 2 am cancelling my previous thread of Button 1 and i am fetching new 5 data for Button 2 and on scrolling it again fetching new 5 data Same as Button 1
but after some time while reloading a tableview my app gets crashes and shows:
Terminating app due to uncaught exception 'NSRangeException', reason: ' -[__NSArrayM objectAtIndex:]: index 28 beyond bounds [0 .. 4]
Here is my code snippet:
- (void)viewDidLoad
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self fetchDataForButton1];
dispatch_async(dispatch_get_main_queue(), ^(void) {
[tableView reloadData];
});
});
}
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
if(it is last row then fetch new 5 data)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Code runs on background thread
[self LoadMoreData];
dispatch_async(dispatch_get_main_queue(), ^(void) {
//Code here is run on the main thread
[_tblList reloadData];
});
});
}else{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self beginBackgroundFetchTask];
[self downloadImage_3:indexPath];
[self endBackgroundFetchTask];
});
}
}
-(void)downloadImage_3:(NSIndexPath *)path{
UIImage *img = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:ImagePath]]];
if (img) {
[dicImages_msg setObject:img forKey:[[msg_array objectAtIndex:path.row] valueForKey:#"Merchant_SmallImage"]];
}
[_tblList performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
}
- (void) beginBackgroundFetchTask
{
self.backgroundFetchTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[self endBackgroundFetchTask];
}];
}
- (void) endBackgroundFetchTask
{
[[UIApplication sharedApplication] endBackgroundTask: self.backgroundFetchTask];
self.backgroundFetchTask = UIBackgroundTaskInvalid;
NSLog(#"ended BackgroundFetchTask");
}
-(void)LoadMoreData
{
//Fetches new 5 data from server...
}
I think the problem maybe at [self downloadImage_3:indexPath]; you call dispatch_async when you at Button1, and the dispatch_async block have not invoked, then you click on Button2, then I guess msg_array is cleared and filled with new 5 object, after that, dispatch_async block is invoked, the path.row for block is 28, whereas, the msg_array has new array content with 5 new objects, then crash.
You should cancel dispatch_async before click on other button, which is impossible for dispatch_async , so you can have a judgement in block:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self beginBackgroundFetchTask];
[self downloadImage_3:indexPath buttonIndex:index];
[self endBackgroundFetchTask];
});
-(void)downloadImage_3:(NSIndexPath *)path buttonIndex:(int)buttonIndex{
if(self.currentSelectIndex != buttonIndex) return; //skip reloadData if not same index
UIImage *img = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:ImagePath]]];
if (img) {
[dicImages_msg setObject:img forKey:[[msg_array objectAtIndex:path.row] valueForKey:#"Merchant_SmallImage"]];
}
[_tblList performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
}
Try this out
1) Use SDWebImage for fetching Images from Server
2) Use Pull Refresher feature for fetching next 5 data.
This will helps to fetch data in background.
Use SDWebImage for fetching Images from Server.
Disable user interaction then go to top on table:
[TableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
then call:
[TableView reloadData];
then enable userintercation.
hope will solve the problem
This is because images take time to load from server and cell gets no data when it is allocated.
You need to implement lazy loading for it.
Here is a good tutorial.
I would not fetch data like that from the cellForRowAtIndexPath method. That is used to display cells.
Instead do it like this:
List your original data.
When the user scrolls past the last cell, show a footer that displays a loading symbol.
at the same time While the footer is displaying a loading symbol, then and ONLY then call the fetchLoadData method, and WAIT
Until you dont hear from the server, continue displaying the loading symbol, dont start adding new cells to prepare for anything, because chances are you might either not get anymore data back, which at this point you have to get rid of the footer cell and let the user know that there is no more data to retrieve, OR if there is more data to display, THEN set up your next 5 cells by doing the following in this order:
adding your new 5 data objects to your data array
updating the number of rows in section using the numberOfRowsInSection delegate method
You will find that your cells if coded correctly will be displayed correctly once you refresh the table using [myTable reloadTable];
AS per your log array is having only 5 count and you are fetching the data as per indexpath.row and in this crash case is 28. So it might be that you are adjusting your cell indexpath row according to your new array which is fetching again and again data on scrolling but in the repetition of 5.
Just put a breakpoint and check whether you are resetting the indexpath row in count of [0 4];
UPDATED:
[[NSThread currentThread] populateDataInBackground];
I have a strange problem with my tableView.
I load data via JSON into my tableView. While the JSON is being requested from the web in another class, I show an activity indicator view in my current view and the tableView is hidden.
I ve got a delegate method, which is called as soon as the json is ready.
-(void)didReceivePlayers:(NSArray *)players {
[activityIndicator stopAnimating];
tableViewPlayers.hidden = false;
startButton.hidden = false;
playersData = [[NSMutableArray alloc] initWithArray:players];
[tableViewPlayers reloadData];
NSLog(#"done reloading");
}
The method is being called perfectly.
The code is pretty straight forward. I hide my activity indicator and show my tableView.
Then I call reloadData. It takes only a few milliseconds. BUT after reloadData, my activityIndicator is still shown and it takes several seconds to show my tableview, although the nslog is being called right away.
I also tried calling reload data in mainThread, but this did not change a thing.
Thanks for your help!
Be sure that the code is being executed on the main thread. You can use the main operation queue like this:
-(void)didReceivePlayers:(NSArray *)players {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[activityIndicator stopAnimating];
tableViewPlayers.hidden = false;
startButton.hidden = false;
playersData = [[NSMutableArray alloc] initWithArray:players];
[tableViewPlayers reloadData];
NSLog(#"done reloading");
}];
}
As the title implies, my UICollectionView doesn't update and display the cells immediately after calling reloadData. Instead, it seems to eventually update my collection view after 30-60 seconds. My setup is as follows:
UICollectionView added to view controller in Storyboard with both delegate and dataSource setup for the view controller and standard outlet setup
numberOfSectionsInRow & cellForItemAtIndexPath are both implemented and reference the prototyped cell and the imageView inside of it
Here is the code that goes to Twitter, get's a timeline, assigns it to a variable, reloads a table view with the tweets and then goes through the tweets to find photos and reloads the collection view with those items.
Even if I comment out the code to display the image, it still doesn't change anything.
SLRequest *timelineRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:timelineURL parameters:timelineParams];
[timelineRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if(responseData) {
JSONDecoder *decoder = [[JSONDecoder alloc] init];
NSArray *timeline = [decoder objectWithData:responseData];
[self setTwitterTableData:timeline];
for(NSDictionary *tweet in [self twitterTableData]) {
if(![tweet valueForKeyPath:#"entities.media"]) { continue; }
for(NSDictionary *photo in [[tweet objectForKey:#"entities"] objectForKey:#"media"]) {
[[self photoStreamArray] addObject:[NSDictionary dictionaryWithObjectsAndKeys:
[photo objectForKey:#"media_url"], #"url",
[NSValue valueWithCGSize:CGSizeMake([[photo valueForKeyPath:#"sizes.large.w"] floatValue], [[photo valueForKeyPath:#"sizes.large.h"] floatValue])], #"size"
, nil]];
}
}
[[self photoStreamCollectionView] reloadData];
}
}];
This is a classic symptom of calling UIKit methods from a background thread. If you view the -[SLRequest performRequestWithHandler:] documentation, it says the handler makes no guarantee of which thread it will be run on.
Wrap your call to reloadData in a block and pass this to dispatch_async(); also pass dispatch_get_main_queue() as the queue argument.
You need to dispatch the update to the main thread:
dispatch_async(dispatch_get_main_queue(), ^{
[self.photoStreamCollectionView reloadData];
});
or in Swift:
dispatch_async(dispatch_get_main_queue(), {
self.photoStreamCollectionView.reloadData()
})
Apple say:You should not call this method in the middle of animation blocks where items are being inserted or deleted. Insertions and deletions automatically cause the table’s data to be updated appropriately.
In face: You should not call this method in the middle of any animation (include UICollectionView in the scrolling).
so, you can:
[self.collectionView setContentOffset:CGPointZero animated:NO];
[self.collectionView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
or mark sure not any animation, and then call reloadData;
or
[self.collectionView performBatchUpdates:^{
//insert, delete, reload, or move operations
} completion:nil];