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.
Related
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).
how can I add an object in my array each time I enter to my TableView
I put this code in viewDidLoad and viewDidAppear methods but it seems to doesn't work
:
- (void)viewDidLoad
{
[super viewDidLoad];
if (!myArray) {
myArray = [[NSMutableArray alloc] init];
}
[peopleListe insertObject:[NSDate date] atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
NSLog(#"%#",myArray);
}
when I put this code in a button it works
Thank you for your help
viewDidLoad is called once, when the view loads.
viewWillAppear is called every time you go into that view.
If you want to do something each time a view appears, put the code in viewWillAppear.
EDIT: It's possible that your array is getting dealloc'd. Try setting a breakpoint in dealloc as a simple way to see if that's the case:
- (void)dealloc {
NSLog(#"BYE!); // <-- put your breakpoint here
}
If it is, you'll have to (a) store your data somewhere else, or (b) keep this view/controller from being dealloced.
Also, who is your tableViewDelegate? That will have to implement methods returning the number of items in the table view and so on. I recommend having a read through the docs to get all those relationships sorted out.
You don't want to have that array as a property/ivar of your view controller. The view controller may, and will, get deallocated when it's not used (e.g. if it's inside the navigation controller, and you tap the "back" button to go to the previous screen.) When the view controller gets deallocated, your array obviously ceases to exist.
I suggest creating keeping that array in a separate place, e.g. in a singleton data object, or even (as a quick short-term solution) your app delegate.
About the code you posted: keep in mind that [UIViewController viewDidLoad] is only called once during the view controller's lifecycle. It may get called more than once, but that would mean that the original instance has been dealloc'd (and your original array is gone).
OK I'm hoping I'm missing something basic here - I am not very expert at this. It should be self-explanatory without example code:
I parse a web-hosted xml file consisting of a list of titles to be displayed in a tableView and associated URLs to pass to a webView when a cell is selected. The parsing happens in the tableView into a dictionary. If I parse on the main thread it works nicely but I'm worried about hanging the UI if the signal is poor. So I wrap the parsing call in a dispatch queue as per examples on here and now it presents an empty table. But if I go back up the view hierarchy and try again (it's embedded in a navigation controller) then it works, there is my table fully populated.
I'm assuming that by using a secondary thread somehow the table is created before the content array is populated. How do I get round this?
Thanks! Andrew
Implement the - (void)parserDidEndDocument:(NSXMLParser *)parser delegate method of NSXMLParser. And call reloadData of your tableView from that method.
- (void)parserDidEndDocument:(NSXMLParser *)parser
{
dispatch_sync(dispatch_get_main_queue(), ^{
[yourTable reloadData];
});
}
Refer NSXMLParserDelegate
If you pares in a dispatch queue you have to update the UI on the main queue.
I am doing something similar. Here is my code:
dispatch_queue_t imgDownloaderQueue = dispatch_queue_create("imageDownloader", NULL);
dispatch_async(imgDownloaderQueue, ^{
NSString *avatarUrlString = [avatarImageDictionary objectForKey:#"url"];
avatarImage = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:avatarUrlString]]];
dispatch_sync(dispatch_get_main_queue(), ^{
id asyncCell = [self.tableView cellForRowAtIndexPath:indexPath];
[[asyncCell avatarImageView] setImage:avatarImage];
});
});
I've got a UITableView that represents a checklist of items. Each item is in a done/undone state.
I'd like to keep all of the done items at the top of the list, so when the user clicks an item that is undone, I'd like to figure out where the row should go (at end of currently-done items list) and move it there.
I'm trying to use -moveRowAtIndexPath:toIndexPath: for my UITableView to do this.
It works well sometimes and not so well at other times.
It seems to work well when the done action kicks off another animation elsewhere on screen. For some reason, this seems to serve as a delay for the -reloadData call.
When that's not true (i.e., the only thing "happening" is the row being marked done and moving), the animation seems to get short-circuited by an automatic call to the UITableView's -reloadData method. That is, the animation begins, but about halfway through, -reloadData is called and the rows snap to their final position. It's fairly jarring from the user's perspective.
I've tracked through my code to verify that I'm not calling -reloadData myself, and it doesn't appear that I'm the one triggering this -reloadData call.
I'm OK with the automatic call to -reloadData and I understand why it's called (though you'd think it might not be necessary, but that's a different issue), but I'd really like it to wait until it completes its animation.
Here's the code I'm using:
NSIndexPath *oldPath = [NSIndexPath indexPathForRow:currentIndex inSection:0];
NSIndexPath *newPath = [NSIndexPath indexPathForRow:newIndex inSection:0];
[tableView beginUpdates];
[checklist removeObject:task];
[checklist insertObject:task atIndex:newIndex];
[tableView moveRowAtIndexPath:oldPath toIndexPath:newPath];
[tableView endUpdates];
Am I screwing something up?
Swift
//---------
extension UITableView {
// Default delay time = 0.5 seconds
// Pass animation time interval, as a parameter argument
func reloadDataAfterDelay(delayTime: TimeInterval = 0.5) -> Void {
self.perform(#selector(self.reloadData), with: nil, afterDelay: delayTime)
}
}
At the request of titaniumdecoy, here's how I got this issue fixed:
I have a UIViewController subclass with an NSTimer that executes once a second, checking the underlying data source for a UITableView for changes that indicate a call to -reloadData is necessary. So, to that subclass I added:
#property (BOOL) IsAnimating;
I set this initially to NO and if the isAnimating property is set to YES, the NSTimer "short-circuits" and skips its normal processing.
So, when I want to run this UITableView animation, I set the isAnimating property to YES and run the animation.
Then, I schedule a selector to run 1 second in the future that will reset isAnimating to NO. The NSTimer will then continue firing and will see an isAnimating of NO (most likely on the second subsequent call to -codeTimerFired) and then find the data source update, kicking off a call to reloadData.
Here's the code for suspending the NSTimer's processing and scheduling the UITableView animation:
// currentIndex and newIndex have already been calculated as the data item's
// original and destination indices (only 1 section in UITableView)
if (currentIndex != newIndex) {
// This item's index has changed, so animate its movement
NSIndexPath *oldPath = [NSIndexPath indexPathForRow:currentIndex inSection:0];
NSIndexPath *newPath = [NSIndexPath indexPathForRow:newIndex inSection:0];
// Set a flag to disable data source processing and calls to [UITableView reloadData]
[[self Owner] setIsAnimating:YES];
// I haven't tested enough, but some documentation leads me to believe
// that this particular call (-moveRowAtIndexPath:toIndexPath:) may NOT need
// to be wrapped in a -beginUpdates/-endUpdates block
[[[self Owner] InstructionsTableView] beginUpdates];
// These lines move the item in my data source
[[[MMAppDelegate singleton] CurrentChecklist] removeObject:[self CellTask]];
[[[MMAppDelegate singleton] CurrentChecklist] insertObject:[self CellTask] atIndex:newIndex];
// This code is the UITableView animation
[[[self Owner] InstructionsTableView] moveRowAtIndexPath:oldPath toIndexPath:newPath];
// Conclude the UITableView animation block
[[[self Owner] InstructionsTableView] endUpdates];
// Schedule a call to re-enable UITableView animation
[[self Owner] performSelector:#selector(setIsAnimating:) withObject:#(NO) afterDelay:1.0];
} else {
// This location hasn't changed, so just tell my owner to reload its data
[[[self Owner] InstructionsTableView] reloadData];
}
Here's the NSTimer method (note how it bails out if isAnimating == YES):
- (void)codeTimerFired {
// This is a subclass of a template subclass...
// super actually has work to do in this method...
[super codeTimerFired];
// If we're in the middle of an animation, don't update!
if ([self IsAnimating]) {
return;
}
// Other data source processing...
// local BOOL to check whether underlying data source has changed
BOOL shouldUpdate = NO;
// code to check if underlying data source has changed...
// ******************************************************
// [CODE REMOVED]
// ******************************************************
// If the underlying data source has changed, update the UITableView
if (shouldUpdate) {
[self reloadTableView]; // <--- This is the main line I wanted to prevent
// since the code that fired to cause the
// UITableView animation will ALWAYS cause
// the underlying data source to change such
// that this line would fire.
}
}