MKMapView - setRegion:animated makes MKMapView unresponsive on iOS7 - objective-c

We've seen an unexpected behaviour in one of our apps - one one screen we're showing annotations on a map view and the user can change the annotations on display by clicking a button.
When we rebuild the app with iOS7 the screen would freeze regularly, i.e. no more user input was possible on the MKMapView once the code below had been called several times (with different sets of annotations) - the view is embedded in both a tab bar and a nav controller and all of their UI elements still worked, but the mapview itself would not accept any user input (pinching/zooming).
The code that displays the annotations is here:
[self.mapView removeAnnotations:self.mapView.annotations];
for (MyObject *my in self.mydata)
{
MyAnnotation *annotation = [MyAnnotationFactory createAnnotationFor:my];
[self.mapView addAnnotation:annotation];
}
CLLocationCoordinate2D mycenter;
mycenter.latitude = -38.967659;
mycenter.longitude = 172.873534;
[self.mapView setRegion:MKCoordinateRegionMake(mycenter, MKCoordinateSpanMake(15, 18))
animated:YES];
[self.mapView setCenterCoordinate:mycenter];
What I found is that by setting the region without animating it, i.e. by changing the above code to
[self.mapView setRegion:MKCoordinateRegionMake(mycenter, MKCoordinateSpanMake(15, 18))
animated:NO];
the problem goes away and the MKMapView behaves nicely on iOS7 as well.
If you have an idea as to why this is happening, and why it is happening only in iOS7 and not for earlier versions, I'd appreciate the clarification.

Also, review your mapView:regionDidChangeAnimated: and mapView:regionWillChangeAnimated: methods. Implementing only one might work for you; one of those might not be needed for you to implement.

Try running the setRegion in a function from the main thread:
[self performSelectorOnMainThread:#selector(animateMapRegion) withObject:nil waitUntilDone:NO];
-(void)animateMapRegion
{
CLLocationCoordinate2D mycenter;
mycenter.latitude = -38.967659;
mycenter.longitude = 172.873534;
[self.mapView setRegion:MKCoordinateRegionMake(mycenter, MKCoordinateSpanMake(15, 18)) animated:animated];
}

Related

Setting frame dose not working in OS7

I am trying to set the frame of my superview when presenting a view, the code i used is
navigationController.modalPresentationStyle = UIModalPresentationPageSheet;
navigationController.view.superview.frame = CGRectMake(32, 20, 1024-(32*2), 748);
[[self navigationController] presentModalViewController:navigationController animated:YES];
the code was working fine in OS6. but when come to OS7 its not working.
This method has been deprecated in iOS 7.
- (void)presentModalViewController:(UIViewController *)modalViewController animated:(BOOL)animated NS_DEPRECATED_IOS(2_0, 6_0);
Use the following :
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^)(void))completion NS_AVAILABLE_IOS(5_0);
If it was working in iOS 6 it was by chance. You are depending on the private view hierarchy used by iOS when presenting modal view controllers, and you've just found out why this is a bad idea. It can, and does, change without warning or documentation. See also, the view hierarchy of a UITableView between iOS 6 and 7.
If you want to make your own style of presented view controller it is safest to write it yourself from scratch rather than hijack one of the existing ones. Try using the custom style of presentation and writing a transition delegate.
Ok i found a solution.,
BOOL ios7 = [UIDeviceHardware isOS7Device];
if(ios7){
navigationController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
}
Now it works fine. :)

purpose of [mv setRegion:region animated:yes];

I'm a beginner for IOS development. So wish someone could explain it with some details. The method is for viewforannotation.
If I keep
[mv setRegion:region animated:yes]
at the end of the function then the code will be in a infinite loop somehow, when I zoom in the map.
If I remove it, the mapview works perfectly fine.
Since I did not write the code, I do not see the purpose of using the line. Could someone tell me is that line necessary?
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id < MKAnnotation >)annotation {
UIButton *abutton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[annotationView setRightCalloutAccessoryView:abutton];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(annotation.coordinate, 8000, 8000);
[mapview setRegion:region animated:YES];
return annotationView;
}
You should not edit the map's location within viewforannotation. viewForAnnotation is called when the map needs to draw the annotations on the map, if while doing that you change the part of the map that the MKMapView is moved there will be different annotations to be drawn, so from within viewForAnnotation you're making it call viewForAnnotation again ==> infinite loop.
So if like you say the code works fine without it, then remove it and be happy. That's my advice for any code. If the application works without it, remove it. No point in bulking up your application with unnecessary code.

Troubles with UISearchBar \ UISearchDisplayViewController

I'm having a hard time with my SearchDisplayViewController on iOS 7.
I have a searchBar hidden over a UITableViewController, like
self.tableView.tableHeaderView = searchBar;
Problem is that when I tap on the searchBar to type in something, then the view starts greying out, and I quickly tap the screen in a random point to dismiss it, coming back to the tableView, the searchBar disappears. Totally. Only on iOS 7 though.
Debugging it, the frame is always the same: 0,0,320,44. But the bar is invisible!
Also tried to do
self.tableView.contentOffset = CGPointMake(0,self.searchDisplayController.searchBar.frame.size.height);
still disappears when I do it quickly.
On iOS 6 it works just fine. Problem is only with iOS 7 as far as I'm seeing.
I don't know what it depends on, has anyone encountered the same problem I have?
As of Double tap UISearchBar with search delegate on iOS 7 causes UISearchBar to disappear, I found the workaround to actually work and solved the bug - for now.
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller
{
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
[self.tableView insertSubview:self.searchDisplayController.searchBar aboveSubview:self.tableView];
}
}
I encountered the same issue, and noticed that searchDisplayControllerDidEndSearch was being called twice. The first time, the superview of self.searchDisplayController.searchBar is the UITableView, and the second time it's still a UIView.
With the accepted answer, I worry about unintended consequences or unneeded overhead from re-inserting the subview every time the search bar is double-tapped, and I also worry about it breaking with future iOS versions. Fortunately, we can take advantage of the superview strangeness like this:
- (void)searchDisplayControllerDidEndSearch:(UISearchDisplayController *)controller {
if (self.tableView != self.searchDisplayController.searchBar.superview) {
[self.tableView insertSubview:self.searchDisplayController.searchBar aboveSubview:self.tableView];
}
}
If I had to guess what was happening, the UISearchBar is automatically creating a temporary UIView as its superview when it's active – this is the view seen when the search is being performed. While the UISearchBar is being dismissed, the superview gets set back to be the UITableView it had before, unless it gets dismissed so quickly that it was never properly initialized, in which case it cleans up improperly and the UITableView never gets the UISearchBar back as its child.
This solution still isn't ideal, and I think Apple must be doing something different in its own apps because their search bar UX feels a bit better. I think it would be better not to handle the second tap in the first place until the UISearchBar was ready. I tried using the other UISearchBarDelegate methods to do this, but I couldn't find an appropriate hook to override the current behavior.
I had the same problem with iOS 7 and I solved it from the apple documentation. The error most people do is that they associate the UISearchBar variable to the self.searchDisplayController.searchBar as the same...! NO NO..! They are 2 different things!!! UISearchBar should be declared and initialized and then wrapped into self.searchDisplayController as searchBar then later wrapped into self.tableView.tableHeaderView by so doing it will not disappear!!!
self.searchBar = [[UISearchBar alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
self.searchDisplayController = [[UISearchDisplayController alloc] initWithSearchBar:self.searchBar contentsController:self];
self.searchDisplayController.delegate = self;
self.searchDisplayController.searchResultsDataSource = self;
self.searchDisplayController.searchResultsDelegate = self;
[self.searchBar setPlaceholder:#"search the hell in me"];
self.tableView.tableHeaderView = self.searchDisplayController.searchBar;
More refined approach for #lehrblogger solution:
- (void)addSearchDisplayControllerBackToTableView {
if ([self.searchDisplayController.searchBar isDescendantOfView:self.tableView] == NO) {
NSLog(#"Search bar is not in current table view, will add it back");
[self.tableView insertSubview:self.searchDisplayController.searchBar aboveSubview:self.tableView];
[self.searchDisplayController setActive:NO animated:YES];
}
}
Reason for this approach: While searching the search bar is moved to search container and the superview of search bar is always some other view other than current table view.
Note: This will dismiss the search, because user tapped more than once on search bar.

Application used MapKit crash on iOS6

I have a strange problem with google map in my application dedicated for iPad (with iOS6). I've made a horizontal scroll view, filled with two views. One is a detail information view (some text, nothing special), and the second view is a view controller with google map. This is the universal scheme in my application (scrollview build from two views) for a few different purposes. The problem occurs when I've started to test the app on a real iPad with iOS6. The application crash when it should view a scroll view. But not immediatly. At start, scroll view is viewed properly. Then I want to build a new scroll view with new datas. It also goes fine, and scroll view is viewed properly. After a few operation like that I've start to receiveing more and more error logs like this:
failed to make complete framebuffer object 8cdd
After a few runs of scrollView, the application crash without any additional errors. Code editor points on main.m file, and the following line:
int retVal = UIApplicationMain(argc, argv, nil, nil);
Please direct me to find what am I doing wrong. Where is the viewDidLoad method from my view controller responsible for viewing the google map:
-(void)viewDidLoad {
mapView.mapType = MKMapTypeSatellite;
mapView.showsUserLocation = YES;
/* ANNOTATION (pin) */
CLLocationCoordinate2D annotationCoord;
annotationCoord.latitude = [self.restaurant.latitude doubleValue];
annotationCoord.longitude = [self.restaurant.longitude doubleValue];
// a pin with the info.
MKPointAnnotation *annotationPoint = [[MKPointAnnotation alloc] init];
annotationPoint.coordinate = annotationCoord;
annotationPoint.title = self.restaurant.name;
// add annotation to the map
[mapView performSelectorOnMainThread:#selector(addAnnotation:)
withObject:annotationPoint
waitUntilDone:YES];
[annotationPoint release];
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance (annotationCoord, 500, 500);
[self.mapView setRegion:region animated:YES];
[super viewDidLoad];
}
and no mater how I push the view controller with google map to the view. It always crash my app :(. I`ve tried like this:
[scrollView addSubview:self.googleMapViewController.view];
or that:
[[self navigationController] pushViewController:self.googleMapViewController animated:YES];
When I run the application on simulator, there`s everything all right. I'm using XCode 4.5.1.
I had the same problem in one of my projects. It was caused by a memory leak. Map views consume alot of memory if you don't delete them. It doesn't appear on simulator because a computer can use much more memory.
Just make sure to get rid of the map views when you no longer need them.
I also recommend running the app with Instruments (Leaks) - it is an extremely useful tool in such cases.

UIPageViewController Traps All UITapGestureRecognizer Events

It's been a long day at the keyboard so I'm reaching out :-)
I have a UIPageViewController in a typical implementation that basically follows Apple's standard template. I am trying to add an overlay that will allow the user to do things like touch a button to jump to certain pages or dismiss the view controller to go to another part of the app.
My problem is that the UIPageViewController is trapping all events from my overlay subview and I am struggling to find a workable solution.
Here's some code to help the example...
In viewDidLoad
// Page creation, pageViewController creation etc....
self.pageViewController.delegate = self;
[self.pageViewController setViewControllers:pagesArray
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:NULL];
self.pageViewController.dataSource = self;
[self addChildViewController:self.pageViewController];
[self.view addSubview:self.pageViewController.view];
// self.overlay being the overlay view
if (!self.overlay)
{
self.overlay = [[MyOverlayClass alloc] init]; // Gets frame etc from class init
[self.view addSubview:self.overlay];
}
This all works great. The overlay gets created, it gets show over the top of the pages of the UIPageViewController as you would expect. When pages flip, they flip underneath the overlay - again just as you would expect.
However, the UIButtons within the self.overlay view never get the tap events. The UIPageViewController responds to all events.
I have tried overriding -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch per the suggestions here without success.
UIPageViewController Gesture recognizers
I have tried manually trapping all events and handling them myself - doesn't work (and to be honest even if it did it would seem like a bit of a hack).
Does anyone have a suggestion on how to trap the events or maybe a better approach to using an overlay over the top of the UIPageViewController.
Any and all help very much appreciated!!
Try to iterate through UIPageViewController.GestureRecognizers and assign self as a delegate for those gesture and implement
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch;
Your code may be like this:
In viewDidLoad
for (UIGestureRecognizer * gesRecog in self.pageViewController.gestureRecognizers)
{
gesRecog.delegate = self;
}
And add the following method:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if (touch.view != self.pageViewController.view]
{
return NO;
}
return YES;
}
The documented way to prevent the UIPageViewController from scrolling is to not assign the dataSource property. If you assign the data source it will move into 'gesture-based' navigation mode which is what you're trying to prevent.
Without a data source you manually provide view controllers when you want to with setViewControllers:direction:animated:completion method and it will move between view controllers on demand.
The above can be deduced from Apple's documentation of UIPageViewController (Overview, second paragraph):
To support gesture-based navigation, you must provide your view controllers using a data source object.