MKMapView with UserLocation crashes when dismissing view controller - ios5

I have a ViewController with a MapView that has UserLocation set and also MKUserTrackingModeFollow set. I have my view controller as the delegate to the mapview.
When I dismiss the Viewcontroller something in the mapview still sends ClientUpdate to my viewcontroller but it crashes as it has now been deallocated.
I tried calling [self.mapView setDelegate:nil] in dealloc but still the same result.
The error is BadAcces when onClientEvent is called on my controller. I am using io5 and ARC.
Any ideas?

I fixed it by putting [self.mapView setDelegate:nil] in viewWillDisappear.

Related

placing popViewControllerAnimated: in viewDidLoad or viewDidAppear will not work

I have a view (viewB) that is pushed in using the navigation controller from another view (viewA) using pushViewController as usual, however for some reason, I want viewB's controller to pop the view using [self.navigationController popViewControllerAnimated:YES]; from inside its viewDidLoad method or viewDidAppear method, but none of them works, i.e. nothing happens (there is no crash in the app), however, i have a UIButton in viewB with IBAction that simply calls [self.navigationController popViewControllerAnimated:YES]; if the button tapped it will work and the view is popped off to the previous view !! this IBAction works if I removed [self.navigationController popViewControllerAnimated:YES]; from viewDidLoad or viewDidAppear methods because the popping will release the current view and all of its sub-views from memory.
the question is how can get the current view (viewB) to be popped off to the previous view (viewA) from inside viewDidLoad or viewDidAppear methods ?
thanks you so much in advance.
Try making method
- (void)popSelf {
[self.navigationController popViewControllerAnimated:YES];
}
In viewDidAppear add
[self performSelector:#selector(popSelf) withObject:nil afterDelay:0.0f];
This will add selector to runloop, so it will be performed after viewDidAppear.

MKMapView crashes app when view controller popped

I have a view controller with an MKMapView that calls
[self.mapView setRegion:region animated:YES];
which repositions the map from A to B.
The view controller which holds the MKMapView is set as the delegate and in
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
I have some code that will trigger another setRegion:animated: to the MKMapView so that the map will zoom in on the new position automatically.
Everything works fine if I popViewControllerAnimated: the view controller AFTER the MKMapView animation is done panning and zooming.
However, when I try to popViewControllerAnimated: the current view controller WHILE the MKMapView is running it's animation, the app crashes with "message sent to deallocated instance".
From the looks of the debugger, I think that MKMapView is trying to call a method from a popped and deallocated delegate.
So I tried
[self.mapView setDelegate:nil];
self.mapView = nil;
in viewDidUnload with no luck. The app still crashes consistently.
The only thing I could think of was to create a separate new delegate class and retain that class from the parent view controller so that the MKMapView would have a delegate to call even after the view controller that contains it is deallocated.
Why is this happening?
Are there any other "clean" options?
A friend helped me get this one.
I implemented my own method for popping the view controller instead of using the default navigation controller's back button. I just had to add [self.mapView setDelegate:nil]; before I popped the view controller.
- (void)goBack
{
[self.mapView setDelegate:nil];
[self.navigationController popViewControllerAnimated:YES];
}
OK, this is the real answer. It's from the Apple doc, but it's missing from MKMapView. It's only found under the documentation for its delegate protocol:
"Before releasing an MKMapView object for which you have set a delegate, remember to set that object’s delegate property to nil. One place you can do this is in the dealloc method where you dispose of the map view."
NOTE: This also applies to UIWebView.
I set the MapView's delegate pointer to nil in the delegate's dealloc method, and our crashes seem to have been eliminated.
My problem was not solved by setting delegate of MKMapView to nil in my view Controller
[self.mapView setDelegate:nil];
I had to make a __strong reference of my UIViewController containing MKMapView in my RootViewController.
__strong <#UIViewController#> *vcNewLocation;
I had done Clustering and was marking the Annotation selected like so mapView.selectAnnotation(annotation, animated: true).
while popping, the deinit method used to crash.
So on press of back button I just add this line mapView.deselectAnnotation(selectedAnnotation, animated: false) and it solved the crash.
The following code is likely to asolve your problem:
-(void) viewWillDisappear:(BOOL)animated
{
self.mapView.delegate = nil;
mapView=Nil;
NSLog(#"viewWillDisappear");
}

iPhone App - dismissing modal view controller doesn't dealloc it

I have a UIViewController (call it NumberTwo) which I presented as a modal view controller from another UIViewController (call it NumberOne). NumberTwo contains a touchesBegan method which listens for touches, and it also has an accelerometer method which listens for device orientation changes in the x, y, or z direction. NumberTwo has a button called "Done" which, when tapped, dismisses itself as a modal view controller:
[self dismissModalViewControllerAnimated:NO];
But it seems as though it's still listening for touches, and it's still listening for accelerations. How can I completely free up NumberTwo when I dismiss it? I tried adding a release call as follows:
[self dismissModalViewControllerAnimated:NO];
[self release];
but that caused a EXEC_BAD_ACCESS.
Did you release the controller after you presented it? E.g. in your method in NumberOneController that presents it, do you have something like:
NumberTwoController * controller = [NumberTwoController alloc] init];
// do stuff to config controller
[self presentModalViewController: controller];
[controller release];
Unless you want to hang on to NumberTwoController for re-use, this would be the usual pattern. The presentModalViewController method ensures that the controller is retained while it's in use. It should then get tidied up when, within NumberTwoController, you call [self dismissModalViewControllerAnimated: NO].
I had a very similar issue that plagued me for days. It turned out that my view controller class wasn't being deallocated when I dismissed it because that view controller had an active NSTimer that wasn't being invalidated (stopped). I was able to kill the timer in viewDidDisappear.
Make sure you are releasing everything you use when you finish with it; The dealloc method is only called when the UIViewController and all of its properties/objects are no longer in use. Never use [self release]; you need to release it from the view controller that created it after you are finished with it.

Accessing a MKMapView through the tab bar

I have a tabbar application and on the first tab I have a MKMapView. What I want to do is from somewhere else in the application, switch the active tab to the mapview and set the mapview's region based on the data in the previous view (the one with the button to switch to the mapview).
What I've tried is:
[self.tabBarController setSelectedView:0];
UIMapViewController *mapView = [self.tabBarController.viewControllers objectAtIndex:0];
[mapView displayBookmarkAnnotation:bookmark];
This just causes the app to crash unable to find the method I created.
I don't think I've chosen the best path to implement this but I'm really not sure how I should go about it.
[Update]
Casting the controller returned by the tabBarController had no effect.
[Solved]
I was trying to cast a UINavigationController to my mapView
[self.tabBarController setSelectedView:0];
UINavigationController *navController = [self.tabBarController.viewControllers objectAtIndex:0];
//if the tab has other views open, return to mapView
[navController popToRootViewControllerAnimated:YES];
UIMapViewController *mapView = (UIMapViewController *)[navController visibleViewController];
[mapView customMessage:object];
Are you sure the main view controller for that tab is not a UINavigationController? If so, you can get the root view controller for that which should be your UIMapViewController.
It would be good to put a direct reference in the AppDelegate though if you are going to be calling it from elsewhere.
Why not route it through your AppDelegate? The AppDelegate can have a UITabBarController and the MKMapView (both wired through interface builder.) The UIButton handler would then also be in the AppDelegate so that it can call -[UITabBarController setSelectedView:] and -[MKMapView setRegion:].
What you want to do is create a subclass or a category of the UITabBarController that
registers for NotificationCenter events that you define
handles the events with a new selector. I generally use do/did naming convention for them.
When the event comes through you set the selectedIndex.

why does viewDidAppear not get triggered?

i have a root view controller that inserts a subview at index 0 at its viewDidLoad method.
i am trying to get the subview to become firstResponder, but can only do this - from my understanding - in the subview's viewDidAppear method.
here's the line of code i added to the root view controller's viewDidLoad method:
[self.view insertSubview: subViewController.view atIndex: 0];
the subviewcontroller has a xib, subViewController.xib, that is shown correctly at runtime. nevertheless, the subViewController's viewDidAppear does not get triggered.
any idea why this happens? any idea how to remedy this - apart from calling viewDidAppear manually (doing so results in failure to become firstResponder)?
thanks,
mbotta
You have to push the view controller on to a navigation stack in order for it's delegate methods to get called. Adding your view controller's view to the subview array won't call them. The first thing you should do is read the View Controller Programming Guide from Apple as this will save you from some headaches you're creating by doing this in a non-standard way.
Instead of adding the view to your root view controller subviews, do this:
SubviewController *controller = [[SubviewController alloc] init];
[[self navigationController] pushViewController:controller animated:YES];
[controller release], controller = nil;
Now your delegate methods will get called. If you don't have a navigation controller as your root view controller, though, this won't work.
If I recall correctly (sorry can't find the place in docs now) -viewDidAppear does not get called for subviews. You must call it manually in the -viewDidAppear method of parent view controller.