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];
});
Related
I have a UICollectionView that's connected to myclass datasource and delegate. When I call [self.collectionview reloaddata] it works normally but after a while (random time and number of times) when I reload the collection again it doesn't work any more.
collection reference delegate and datasource are correctly defined.
self.devicesCollectionViewDataSource= devices;
self.devicesCollectionView.delegate = self;
self.devicesCollectionView.dataSource = self;
[self.devicesCollectionView reloadData];
Overwrite the setDelegate method of your collection view subclass, set a breakpoint and observe who's changing it.
-(void)setDelegate:(id<YOUR_PROTOCOL>)delegate {
_delegate = delegate; // breakpoint here -> look at the trace log, it should give you the caller that's overwriting your delegate
}
Clarification: this doesn't solve your problem per se, but at least you know where to look
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];
}
Can someone clarify something for me. Apple documentation for UITableView says
`UITableView` overrides the layoutSubviews method of `UIView` so that it calls reloadData only when you create a new instance of `UITableView` or when you assign a new data source.
I'm getting the following behaviour:
In my viewDidLoad, I have tableView.dataSource = self.
In my getDataFromDatabase, I call [tableView reloadData]. This seems to work fine, but I think what's happening is the table is being populated with a blank array, then, when the db call comes back, it's reloading it with an array with data.
I'm trying to do the following:
In viewDidLoad, remove tableView.dataSource = self;, and add it in the getDataFromDatabase method. e.g.
(void)getDataFromDatabase
{
[getData completionHandler^() // an async db call
{
tableView.dataSource = self;
}];
}
This doesn't seem to be working. The table is not populated. It works if I add [tableView reloadData] after setting the dataSource = self, but from the documentation, shouldn't it call reloadData automatically when I set the dataSource = self "it calls reloadData only when you create a new instance of UITableView or when you assign a new data source." ?
Is your "completionHandler" running on main thread? If not or you are not sure about that, try executing your code on main thread. Because according to apple documentation, the result of not updating the UI on main thread is uncertain. You can try this:
[getData completionHandler^() // an async db call
{
dispatch_async(dispatch_get_main_queue(), ^{
tableView.dataSource = self;
});
}];
I am designing a one-to-one chatting interface using table view. This table view is modified to show bubbles as some new message arrives. The new message arrives through a push-notification in this case. I call following code in my function which receives message through the push notification:
-(void)messageReceived: (NSString *)message{
_message=[message retain];
[tableView reloadData];
}
However, it seems this does not reload my table view.
If I place the call for reloadData in the viewDidAppear function, it reloads fine. It also reloads fine, if I place the reloadData call in a function whose return type is IBAction (ex: a function binding to button click)
What could be the reason for reloadData to not get triggered through custom declared functions ?
reloaddata method is called but the trick here that you didn't add the incoming message to the datasource that the tableview load from !
may be you have not Connect the Tableview with table view Delegates and Datasource
Objective-C
#interface YourClass : UIViewController <UITextFieldDelegate, UITextViewDelegate>
yourtableview.delegate = self;
yourtableview.dataSource = self;
[tableView reloadData];
Swift 3
class YourClass: UIViewController , UITableViewDelegate, UITableViewDataSource
yourtableview.delegate = self
yourtableview.dataSource = self
yourtableview.reloadData()
the other way is! for Swift and Objective-C both.
Right Click on the Table view and drag and drop the delegates.
I am new in iphone using MKMapView to load a google's map in my application .But its throwing an exception "MKMapView must be initialized on the main thread.". So where should I initialise my MKMapView object. I am initializing in viewDidLoad().
thanks in advance.....
The reason it isn't being creating on the main thread is one of 2 options
The View that creates it lives on one of the background threads
You are are calling performSelectorInBackground: to create your view
To perform a function on the Main Thread call performSelectorOnMainThread:
Off-topic: If the function requires more then 1 parameter, change it to an NSDictionary and load all of your settings into the Dictionary and pass it in eg
NSDictionary *params = ...... //load your parameters into here
[myMapView performSelectorOnMainThread:#selector(initMap:)
withObject:params
waitUntilDone:YES];
Also read the following First and Second to get a better understanding of multithreading in iOS
With Swift 3, the following will ensure your function runs on the main thread.
OperationQueue.main.addOperation{"your segue or function call"}
I got this error:
MKMapView must be initialized on the main thread
When I tried to change the content of a customized UITableViewCell in this way:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[myIndexPath] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
The UITableviewCell in the corresponding indexpath contained a MKMapView that I tried to change its region.
myTabelViewCell *cell = [self.tableView cellForRowAtIndexPath:myIndexPath];
[cell setContentWithLatitude:currentLatitude longitude:currentLongitude];
Use the above code instead of [self.tableView reloadData] to refresh just one cell.