how to open OpenStreetmap in mkmapview? -iphone - iphone

I have gone through certain related answers but dont seem to get correct answer or the answer I need.
As we open googlemap in mkmap view I want to open openstreet map in mkmapview.
If there is any link or sample code showing it please pass it on.
Thanks in advance

MKMapView conforms to Google Map terms and conditions so it uses only google map. You cant integrate OpenStreetMap just like that into MKMapView. Google code has a API RouteME which renders OpenStreetMap in iphone.
RouteMe also provide a good documentation how to include into our project. So feel free to use that.

Import these Frameworks:
#import <MapKit/MapKit.h>
Include this delegate
#interface ViewController () <MKMapViewDelegate>
Add this code to your preexisting viewDidLoad method
(void)viewDidLoad
{
// Tile system URL template goes here
NSString *template = #"http://tile.openstreetmap.org/{z}/{x}/{y}.png";
// Associating overlay with URL template
MKTileOverlay *overlay = [[MKTileOverlay alloc] initWithURLTemplate:template];
// Allowing overlays on MKMapView & disabling Apple map data
overlay.canReplaceMapContent = YES;
// Adding overlay to MKMapView above Apple lables
[self.mapView addOverlay:overlay level:MKOverlayLevelAboveLabels];
// Linking the delegate to allow the next method to be called
self.mapView.delegate = self;
}
And this somewhere in your class that’s mapView’s delegate (most likely a view controller).
(MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
if ([overlay isKindOfClass:[MKTileOverlay class]]) {
// Overlay the tile with the new template
return [[MKTileOverlayRenderer alloc] initWithTileOverlay:overlay];
}
return nil;
}

Related

MapKit - iOS 13 - How to show pin annotations on top on the user location annotation?

Currently, when I try to add an annotation to the map in the user's location the annotation is not showing up. When I set the annotation (using the same code) besides the user's location it does show up.
The code:
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate // set coordinate
annotation.title = "Title"
annotation.subtitle = "Subtitle"
mapView.addAnnotation(annotation)
same issue here, it was working fine with iOS 12, now all annotations near the current location marker are not visible anymore. Critical information might be near the user location and needs to be displayed. This is mostly bad when you zoom out the map, as you don't know anymore if there is data since all annotations comes near the user location...
Is there a document that indicates this change from apple? I didn't find any... thanks in advance for someone that finds it.
If there is no options to turn off this behavior, the only workaround I can think of is to manually add the current location annotation ourselves...
In the callback -mapView:viewForAnnotation on your MKMapViewDelegate be sure that the MKAnnotationView you return leaves its displayPriority as its default value which is MKFeatureDisplayPriorityRequired otherwise MapKit clusters your annotation view with the user location.
I have a fix for those having issues. I was having issues in my production app as well because I was just adding the annotations to the map and never calling the viewForAnnoation method. I created a sample project and uploaded it to Github so you can review the code and see what I did. In short, you need to create a class that subclasses NSObject and conforms to the MKAnnotation protocol. If using MKLocalsearch like my project, pass the MKMapItem object back and create a new instance of the new class. You will also need to create a class and subclass MKMarkerAnnotationView.
class CustomMKMarkerSubclass: MKMarkerAnnotationView {
override var annotation: MKAnnotation? {
willSet {
if let _ = newValue as? MapLocation {
displayPriority = MKFeatureDisplayPriority.required
canShowCallout = true
}
}
}
}
For more detail, check the project below.
MapKit Demo Project
Using MKPinAnnotationView instead of MKAnnotationView has fixed my issue
- (MKAnnotationView *)mapView:(MKMapView *)tempMapView viewForAnnotation:(id <MKAnnotation>)tempAnnotation{
if ([tempAnnotation isKindOfClass:[MKUserLocation class]]){
return nil;
}
NSString *identifer = #"Identifer";
MKPinAnnotationView *dPV = [[MKPinAnnotationView alloc] initWithAnnotation:tempAnnotation reuseIdentifier:identifer];
[dPV setSelected:YES];
[dPV setTag:102];
dPV.canShowCallout = YES;
[dPV setSelected:YES];
dPV.draggable = YES;
return dPV;}
It seems like iOS 14 fixed this issue

how to let users of an app in mapview drop multiple pins?

How do i code a map view in which users can drop multiple pins, make annotations and add pictures?
i know how to code a map view in Xcode and how to put a pre-set pin however i want to be able to code something a bit more complex as it works in relation to an app i'm hoping to create!
1: how do i let a user of the app drop multiple pins to specify a certain location?
2: once the user has dropped a pin i want that he is able to add annotations and pictures?
i have loked extensively into this and although i have found how to code basic maps into an app i have not been able to code the follow up parts!
thank you for reading my question and would really appreciate any suggestions that can be given. if you think there is a useful youtube video or a blog that you think could help me please put it at the bottom and i will look through it!
First make an Annotation class that implements the <MKAnnotation> protocol:
#interface Annotation : NSObject <MKAnnotation>
{
CLLocationCoordinate2D location;
NSString *title;
NSString *subtitle;
NSString *otherInfo;
UIImage *image;
// etc...any other info you want to include with your annotation
}
Then when you want to drop a pin:
-(void)dropPin:(CLLocationCoordinate2D *)location (NSString *)title (NSString *)subtitle (NSString *)otherInfo (UIImage *)image
{
Annotation *pin = [Annotation annotationWithCoordinate:location];
pin.title = title;
pin.subtitle = subtitle;
pin.otherInfo = otherInfo;
pin.image = image;
[self.mapView addAnnotation:pin];
}
You'll probably also want to store your dropped pins in an array or something so you can keep track of them and reference them later.
Take a look at the MapKit documentation for the MKAnnotation protocol for more information and examples. Also read this tutorial, which gives an excellent overview of how to use the MapKit objects and protocols, including annotations.

UIPopoverController for iphone not working?

I need to use a UIPopOverController for my iPhone app ,i searched stackoverflow someone said UIPopoverController does not run on iphone iphone device WHY?.when i run on iphone device
i got this error reason: '-[UIPopoverController initWithContentViewController:]
called when not running under UIUserInterfaceIdiomPad.'
-(void)btnSetRemainderTapped:(UIButton *)button
{
setReminderView =[[SetRemainderView alloc]initWithNibName:#"SetRemainderView" bundle:[NSBundle mainBundle]];
setReminderView.contentSizeForViewInPopover = CGSizeMake(setReminderView.view.frame.size.width, setReminderView.view.frame.size.height);
setReminderView.delegate = self;
popOverController = [[UIPopoverController alloc]
initWithContentViewController:setReminderView] ;
CGRect rect = CGRectMake(self.view.frame.size.width/2, self.view.frame.size.height/2, 1, 1);
[popOverController presentPopoverFromRect:rect
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
can any one help me?
You CAN use popoverController in iPhone apps.
1. Create a category
// UIPopoverController+iPhone.h file
#interface UIPopoverController (iPhone)
+ (BOOL)_popoversDisabled;
#end
// UIPopoverController+iPhone.m file
#implementation UIPopoverController (iPhone)
+ (BOOL)_popoversDisabled {
return NO;
}
#end
2. Import it to your class and use popover in iPhone as usual.
But remember that this is private method and Apple can reject your app. But I know people who use this normally and Apple published their apps.
Edit: As stated by Soberman, since iOS 8 it is possible to present popovers on iPhone using public APIs, so this answer is probably not relevant anymore.
As stated in Apple's documentation on UIPopoverController:
Popover controllers are for use exclusively on iPad devices.
So there is no way to use this class in iPhone application unfortunately. But there are a couple of custom third-party implementations of the functionality provided by UIPopoverController which add iPhone support and more. See https://github.com/50pixels/FPPopover for example.
Edit: There also is another highly customizable popover implementation for both iPhone/iPad worth checking out: https://github.com/nicolaschengdev/WYPopoverController.
Since iOS8 we are now able to create popovers, that will be the same on iPhone, as on iPad, which would be especially awesome for those who make universal apps, thus no need to make separate views or code.
You can get the class as well as demo project here: https://github.com/soberman/ARSPopover
All you need to do is subclass UIViewController, conform to the UIPopoverPresentationControllerDelegate protocol and set desired modalPresentationStyle along with the delegate value:
// This is your CustomPopoverController.m
#interface CustomPopoverController () <UIPopoverPresentationControllerDelegate>
#end
#implementation CustomPopoverController.m
- (instancetype)init {
if (self = [super init]) {
self.modalPresentationStyle = UIModalPresentationPopover;
self.popoverPresentationController.delegate = self;
}
return self;
}
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
return UIModalPresentationNone; //You have to specify this particular value in order to make it work on iPhone.
}
Afterwards, instantiate your newly created subclass in the method from which you want to show it and assign two more values to sourceView and sourceRect. It looks like this:
CustomPopoverController *popoverController = [[CustomPopoverController alloc] init];
popoverController.popoverPresentationController.sourceView = sourceView; //The view containing the anchor rectangle for the popover.
popoverController.popoverPresentationController.sourceRect = CGRectMake(384, 40, 0, 0); //The rectangle in the specified view in which to anchor the popover.
[self presentViewController:popoverController animated:YES completion:nil];
And there you have it, nice, neat blurred popover.
So #Sobermans answer didn't really solve the issue from start to finish for me so I want to detail how I got it done using the docs. That being said I do like the idea of using your own presentation controller subclass to manage all of the customisation you want to exhibit.
1. Create your controller to present
The first step is instantiating the controller you want to present:
let vc: UIViewController = ...
vc.modalPresentationStyle = .Popover
vc.preferredContentSize = CGSize(width: CGRectGetWidth(view.bounds)/2, height: 100)
Now we have a controller with the popover presentation style and an arbitrary content size.
2. Implement adaptivePresentationStyleForPresentationController
By default UIPopoverPresentationController will present on full screen on iPhone so to prevent this behaviour you need to force the adaptive presentation style to none.
First we set the delegate of the popover presentation controller
vc.popoverPresentationController.delegate = self;
Then we implement UIPopoverPresentationControllerDelegate
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None;
}
3. Present and configure popup
First we need to call presentViewController and only after that can we configure the popover:
presentViewController(vc, animated:true, completion:nil)
if let popover = vc.popoverPresentationController {
popover.permittedArrowDirections = .Right | .Left
popover.sourceView = button
popover.sourceRect = button.bounds
}
Use a custom popover controller, such as:
https://github.com/sammcewan/WYPopoverController
(this seems to be the best supported one that I have found).
I ended up creating my custom tooltip/popover class.
Can be initalised with any content view and dynamically adjusts it's frame.
Hope it helps.
https://github.com/akeara/AKETooltip
If you want to do it in Swift, I believe the code is the following:
extension UIPopoverController {
class var _popoversDisabled : Bool {
get { return false }
}
}
Edit: It is working in Xcode 6 beta 4 on iPhone with iOs7.1
This is a really interesting (and depressing) thread to read. I can't believe Apple prevents popup dialogs on iPhones, with absolutely no justification.
And, it's true, on iOS 8, if you try to work around this limitation, it'll make your popups appear as a full-screen modal dialog.
The following excellent webpage describes "How Apple Cheats" to let its own iBooks and iTunes apps break its own rules, and allow popups - but just from within their own iPhone apps.
HowAppleCheats
Have a read (warning: it'll make you hate Apple & XCode even more..)
Want to get around the "UIPopoverController called when not running under UIUserInterfaceIdiomPad" error on iOS 8 ?
Simple.
Just go into your .plist file, and change the Bundle ID to "com.apple.itunesu" to make XCode think that your app is actually iTunes.
Then your popup will work fine.
(Sigh.)
The alternative way of doing this is to directly add your UIViewController to your screen.
In this example, I wanted a "helper screen" to appear on top of my iPhone screen. It's a UIViewController, it is stored in it's own .xib file, and it has a few lines to add a pretty border:
- (void)viewDidLoad {
[super viewDidLoad];
// Give our popup a pretty curved border
self.view.layer.borderColor = [[UIColor blueColor] CGColor];
self.view.layer.borderWidth = 1.0;
self.view.layer.cornerRadius = 8;
}
To display it, I simply create an instance of this UIViewController, add it to my screen, then center it:
-(void)showHelperScreen
{
if (self.helperScreen == nil)
{
// Add the popup UIViewController to our screen
self.helperScreen = [[HelperViewController alloc] init];
[self.view addSubview:self.helperScreen.view];
}
// Center the popup in the middle of the screen
CGSize screenSize = [[UIScreen mainScreen] applicationFrame].size;
self.helperScreen.view.center = CGPointMake(screenSize.width/2, screenSize.height/2);
}
Of course, I also needed to add some code to make the popup disappear when the user taps outside of it, but this does at least show that you can (safely) display popups on an iPhone, even if your app isn't specifically called iTunes or iBook.
Voila.
Hope this helps, and if anyone needs me, I'll be back in my safe, happy place (Visual Studio, in other words).

SetUserTrackingMode not working

I am programming a map app on iPhone and want the map to rotate as the user changes his direction. I have read most of the posts on stackoverflow. Most of them suggest the use of setUserTrackingMode with MKUserTrackingModeFollowWithHeading if we are working with iOS 5 or later. This does not seem to work with me for some reason. Following is my code:
-(IBAction)getLocation //This is a button
{
mapView.showsUserLocation=YES; //mapView is the instance of MKMapView
[mapView setUserTrackingMode:MKUserTrackingModeFollowWithHeading animated:YES];
}
This only shows the user location but if I move the phone, it doesn't rotate.
One more thing is, I downloaded a project from internet, and I included this line. It worked there only for the first time. I have no idea why this is happening.
Any suggestions?
You need to wait for the 'MapView' finish loading...
follow:
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView {
mapView.userTrackingMode = MKUserTrackingModeFollow;
}
follow & heading:
- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView {
mapView.userTrackingMode = MKUserTrackingModeFollowWithHeading;
}
The easier way to do this is to include an MKUserTrackingBarButtonItem instead of creating your own button. It acts exactly the same as the button in the iOS 5 Maps app and is easy to set up.
Here's how to use it:
// You should have an outlet to your map view called mapView
MKUserTrackingBarButtonItem *userTrackingButton;
userTrackingButton = [[MKUserTrackingBarButtonItem alloc] initWithMapView:self.mapView];
// You need an outlet to your toolbar too
[self.toolbar setItems:[NSArray arrayWithObject:userTrackingButton]];
Change "Animated" to "animated" and try again

Customize the MKAnnotationView callout

I want to create a custom MKAnnotationView callout as shown in this image. I have tested several solutions but they only allow customization of the left/right images and title/subtitle. Can anybody please give me some source code or tutorial link for it?
Currently I am clueless. Please help.
I understand you want a pin with a custom callout.
We can't create a custom callout, but we can create an annotation with a completely customized view. So the trick is to add a second annotation when the first is selected, and make the 2nd annotation view look like a callout bubble.
This is the solution posted by users djibouti33 and jacob-jennings in the answer: MKAnnotationView - Lock custom annotation view to pin on location updates, which in turn is based in a blog post from Asynchrony Solutions. For explanation purposes, here is some UML from a forked project:
This is a big hack, but also the cleanest way I've seen to implement custom annotations.
Start with a NSObject "Content" class which has a coordinate, the class of the callout view to use (in the UML is AnnotationView, but you can create more and set them here), and a dictionary of random values with the title, photo url, etc. Use this class to initialize a MKAnnotation "Annotation" object.
#import <MapKit/MapKit.h>
#interface Content : NSObject
#property (nonatomic,assign) CLLocationCoordinate2D coordinate;
// ...
#interface Annotation : NSObject <MKAnnotation, AnnotationProtocol>
-(id) initWithContent:(Content*)content;
// ...
The Annotation implements AnnotationProtocol to announce it wants to handle the creation of its own MKAnnotationView. That is, your MKMapViewDelegate should have code like this:
- (MKAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id<MKAnnotation>)annotation
{
// if this is a custom annotation, delegate the implementation of the view
if ([annotation conformsToProtocol:#protocol(AnnotationProtocol)]) {
return [((NSObject<AnnotationProtocol>*)annotation) annotationViewInMap:mapView];
} else {
// else, return a standard annotation view
// ...
}
}
The view returned will be of type AnnotationView, which implements AnnotationViewProtocol to announce that it wants to handle selection/deselection. Therefore, in your map view controller, the methods mapView:didSelectAnnotationView: and mapView:didDeselectAnnotationView: should delegate in a similar way to what we saw before.
When the annotation is selected, a second annotation (CalloutAnnotation) is added, which follows the same behaviour, but this time the view returned (CalloutView) is initialized from a XIB, and contains Core Graphics code (in BaseCalloutView) to animate and replicate a callout.
The initializer of the CalloutView class:
- (id)initWithAnnotation:(CalloutAnnotation*)annotation
{
NSString *identifier = NSStringFromClass([self class]);
self = [super initWithAnnotation:annotation reuseIdentifier:identifier];
if (self!=nil){
[[NSBundle mainBundle] loadNibNamed:identifier owner:self options:nil];
// prevent the tap and double tap from reaching views underneath
UITapGestureRecognizer *tapGestureRecognizer = ...
}
return self;
}
To be able to push another view controller from the callout view I used notifications.
The SO answer I linked at the top contains two complete projects implementing this code (class names may differ). I have another project using the UML above at https://github.com/j4n0/callout.
I added custom UIButton in MKAnnotationView. And on click of that button I have shown popOver with rootViewController with the view similar as you have shown above.
I know this question is from 2011 but for people who still find it in a search:
In iOS 9 you have MKAnnotationView.detailCalloutAccessoryView which entirely replaces the standard callout.