I am using a CollectionView which displays an array of objects.
On clicking a button i fill this array with a new set of data.
I want to refresh the CollectionView with this data.
Is there any statement to update this instead of comparing each items and selectively deleting and adding? The reloadData usually ends up in the following error.
CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION
In Short, I am looking for the following steps...
1)Fill the datasource array, show the data.
2)Fill the datasource array with new data, refresh the CollectionView to show the data.
Thanks in Advance
Try - (void)performBatchUpdates:(void (^)(void))updates completion:(void (^)(BOOL finished))completion.
In your case, you want "an all new set of data", so to speak, so e.g:
[myCV performBatchUpdates:^{
// one of:
// a)
[myCV deleteSection:someIndexSetForTheEntireSection];
[myRealDataSource empty:someIndexSetForTheEntireSection];
//
// OR b)
[myCV deleteItemsAtIndexPaths:someSetOfIndexPaths];
[myRealDataSource removeIndexPaths:someSetOfIndexPaths];
// Either case:
NSArray *indexPaths = [myRealDataSource getNewDataAndReturnIndexPaths];
// if a)
[myCV insertSections:newIndexSetForNewSection];
// Either case:
[myCV insertItemsAtIndexPaths:newIndexSetForInsertions];
}
completion:^(BOOL finished) {
NSLog(#"Done.");
// Maybe signal something else if you want.
}];
performBatchUpdates:completion: will expect the deletions & insertions from the original data source check entering the function to add up to the correct data source size leaving the method. It will loudly complain otherwise.
If you only have one section (section 0), you can be much more general than specific index paths if you are always "removing everything" and "inserting a complete new set".
Another option to to use KVO to listen on insertions and removals from the data source and simply reloadData, reloadItemsAtIndexPaths: or reloadSections: as appropriate.
I prefer the reactive KVO version, as I tend to use collection views with Core Data, and not pseudo-static or in-memory NSArray's.
To figure out the CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION issue, I'd setup a breakpoint on all exceptions, and try to discover what is really triggering the issue. Likely your datasource is gone and there's a bad access when you try to read/write from it.
Suppose you arrive on your view then you can add data to your array in viewDidLoad method like so:
-(void)viewDidLoad
{
[super viewDidLoad];
// If you have already data
self.arr_sample=(NSArray *)array;
/*
When you want to download data from server than you have to call reloadData
method of collection because all delegate method already called before view load.
So loading data will take time to load data than you have to call all delegate
method of collectionview by calling reloadData method.
*/
[self loadData];
// Do any additional setup after loading the view from its nib.
}
but first of all you have set the delegate of collectionview .
do you want to download data from server than you can call reloaddata method of collection view. such as
-(void)loadData
{
// Do your downloading code her and call to reload data method of collectionview
[collectionview reloadData];
}
now again do you want to refill your array with new data on your button click than you can do
-(void)refillData
{
// Download new data code here and follow
[array removeAllObjects];
array=(NSArray *)newarray;
[collectionview reloadData];
}
Related
My application list an array of feeds. Feeds are displayed in a tableview. Each cell has a likebutton and a feed data object. When the likebutton is clicked, an API call will happen and it is written inside the table cell subclass. On success of the API call I need to update the likebutton image and feed data object. But if I scroll the tableview after starting the API call and before receiving Onsuccess, the data object and likebutton I refer in the Onsuccess method will have a different index(due to cell reuse). How can I refer the data object when the API call was started? My code is given below.
#import "FeedCell.h"
- (IBAction)likeAction:(id)sender
{
[APIManager unlikeORunlikePost:self.feedObject.entityID withSuccess:^(id response)
{
//Here I want to get the 'self.feedObject' which was passed to the API manager
//If I try to get 'self.feedObject' , that object will be different from what I passed initially if the tableview is scrolled before entering this success block
}
andFailure:^(NSString *error)
{
}];
}
weakly capture the feed object in the block and compare it to the cells current feed object, if they are the same then the cell hasn't been reused.
The block is only capturing self, which is the cell, and as you've found this is changed if you have scrolled the table and the cell is reused.
Capture the specific data object instead:
- (IBAction)likeAction:(id)sender
{
FeedObject *feedObject = self.feedObject;
[APIManager unlikeORunlikePost:feedObject.entityID withSuccess:^(id response)
{
[feedObject doSomething]; // This will now be the original object
//Here I want to get the 'self.feedObject' which was passed to the API manager
//If I try to get 'self.feedObject' , that object will be different from what I passed initially if the tableview is scrolled before entering this success block
}
andFailure:^(NSString *error)
{
}];
}
However:
Should your cell be doing much more after this completion call? Isn't it the responsibility of a different object?
Returning the affected object in the completion block would be neater
I have a tabBarView which have two tableViews. each of these tableViews will represent some news from a remote server. I want to populate tableView's datasource when tableViewController's init method is called. so I have put the needed networking operation inside init method. My init method is this:
- (instancetype) init{
self = [super init];
[NewsManager fetch:10 remoteNewsOfLanguage:#"fa" withOffsett:1 andCompletionHandler:^(NSMutableArray *news) {
self.newsList = news;
}];
self.tabBarItem.title = #"my title";
return self;
}
newsList is an array holding news loaded from server.
But when I run my project the order of invocation for tableViewController's methods is like the following:
tableViewController's init method is called and finished (but the completion handler block is not called yet)
tableViewController's viewDidLoad method is called ( it is called when the tableViewController is added to tabBarView's viewControllers array)
tableViewController's delegate method tableView:numberOfRowsInSection is called
the network operation's completionHandler block is called and the newsList array is set to the retrieved news from server
So my problem is that before my newsList array is populated the method tableView:numberOfRowsInSection is called and so my tableView is not filled with any news. How should I solve this issue?
Thanks
you should reload table data after you get data from server. then only your table will show updated data.
[NewsManager fetch:10 remoteNewsOfLanguage:#"fa" withOffsett:1 andCompletionHandler:^(NSMutableArray *news) {
self.newsList = news;
[yourTableview reloadData];//add this line
}];
The added line does the job and makes the new data to be loaded in the tableView but there is a small point that I think you should consider
[tableView reloadData]
will be executed in a thread other than mainThread and this will cause a 5 to 10 seconds delay for the data to be loaded on the tableView.
to prevent this you should somehow tell it to run the reloadData method on the main thread. this is done with the dispatch_async. So you should call [tableView reloadData] like this:
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
I have a UITableView where each section contains a single row which each contain a UICollectionView. The UITableView is the data source and delegate to a Core Data database. Updates to the database call the NSFetchedResultsController's controller: didChangeObject: method which queues blocks to update the relevant collection as so:
switch (type) {
case NSFetchedResultsChangeInsert: {
[self.blockOperation addExecutionBlock:^{
[collectionView insertSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] ];
}];
break;
}...
I then want to execute the blocks here as a batch:
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
[collectionView performBatchUpdates:^{
[self.blockOperation start];
} completion:^(BOOL finished) {
// Do whatever
}];
}
This is using Ash Furrow's and Blake Watters' techniques for hooking up a core data database to a UICollectionView.
My question is, how do I get access to the correct collectionView object in -controllerdidChangeContent:? Each block within self.blockOperation has the relevant collectionView, but I don't know how to parse it out from the NSBlockOperation's executionBlocks property, or even if that's the best way to get at it. Ash and Blake's example only has one UICollectionView whereas I have many.
You don't want to use only a single block operation because you won't be able to separate the data afterwards. Instead, use one block operation per collection view (which is also per section). Add each to a dictionary with the associated index path as the key (check it one already exists and create / update as necessary). When you run the blocks, iterate the dictionary, get the collection view for the index path (key) and run the block operation (value).
This is my first question so excuse me for being a newbie.
I am working with a CollectionView that shows images downloaded from the internet. The problem appears when I try to do it asynchronously.
#interface myViewController
{
NSArray *ArrayOfSections
}
#end
-(void)viewDidLoad{
[self performSelectorInBackground:#selector(refreshImages) withObject:nil];
}
-(void)refreshImages{
... //Get information from the net
NSArray internetInfo = ...;
[self performSelectorOnMainThread:#selector(refreshCollectionView:) withObject:internetInfo waitUntilDone:NO];
}
-(void)refreshCollectionView:(NSArray tempArray){
ArrayOfSections = tempArray;
}
This code is not working. It shows an empty CollectionView, although I have double checked that the information stored on ArrayOfSections is correct.
Moreover, if I do it synchronously (I change only viewDidLoad).
-(void)viewDidLoad{
[self refreshImage];
}
Everything works fine. I am going bananas. Please help
I think it's because you're not telling the collection view to reload. Your refresh method updates the model but not the view.
If you're fetching the data on a background thread, the main thread can continue it's lifecycle, which involves querying the collection view datasource and delegate methods then updating the view, but it will be doing this too soon in your case, as the model isn't ready. That's why you need to tell it to do that again, when the model is ready, at the end of your data fetch. Since you block the thread when doing it synchronously, it won't reach the collection view methods until the model is ready, which is why it works that way.
I have a ViewController and a separate UITableViewController. Now there is an array loaded in the applications appDelegate file, but I do not want to automatically load this. Now I have this code from when I was learning Core Data (still a progress) that detects if the Database is empty, if so then it would insert a default array by calling this method.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[self setupFetchedResultsController];
if (![[self.fetchedResultsController fetchedObjects] count] > 0 ) {
NSLog(#"!!!!! ~~> There's nothing in the database so defaults will be inserted");
[self importCoreDataDefaultRoles];
}
else {
NSLog(#"There's stuff in the database so skipping the import of default data");
}
Now as you can see the if statement checks if the count returned is greater than 0.
My question is Can you check this without the use of Core Data? Like with any tableView.
My goal is for the end user to be able to load data into the TableView witha normal NSMutabelArray, but do so via an IBAction button, rather than loading automatically in the viewDidLoad - Just setting myself an exercise:-)
Cheers Jeff
You can at the beginning show default data in the table using an empty array and the when user clicks the button you can insert data in the source array and refresh the table with
[tableView reloadData];