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.
Related
I am stuck on what is supposed to be a very simple thing to do: have a Core Data Entity store / display (through bindings) an image assigned as a transformable attribute.
I've read many related posts on the Stack (e.g., see here and here), but am still having trouble with it, after having developed sample code and researched other articles (e.g., see here as well as here). This is related to my earlier question, which I still have not resolved.
I created a simple doc-based Core Data App to demonstrate the problem. The Core Data managed object is called "TheEntity" and the attribute "theImageAtt." The entity as defined in Core Data is shown below (ImageValueTransformer is the NSValueTransformer):
I let XCode generate the NSManagedObject subclass header and implementation files (I left out the code for the "name" attribute to make it simpler):
// TheEntity.h
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "ImageValueTransformer.h"
#interface TheEntity : NSManagedObject
#property (nonatomic, retain) NSImage * theImageAtt;
#end
-----------------------
// TheEntity.m
#import "TheEntity.h"
#implementation TheEntity
#dynamic theImageAtt;
#end
Below are the header and implementation files for my "ImageValueTransformer." Lots of examples of this on the Stack and elsewhere (the tiff rep is arbitrary).
// ImageValueTransformer.h
#import <Foundation/Foundation.h>
#interface ImageValueTransformer : NSValueTransformer
#end
-------------------------------
// ImageValueTransformer.m
#import "ImageValueTransformer.h"
#implementation ImageValueTransformer
+ (BOOL)allowsReverseTransformation {return YES;}
+ (Class)transformedValueClass {
return [NSData class]; // have also tried: return [NSImage class];
}
- (id)transformedValue:(id)value {
NSData *data = [value TIFFRepresentation];
return data;
}
- (id)reverseTransformedValue:(id)value {
NSImage *imageRep = [[NSImage alloc] initWithData:value];
return imageRep;
}
#end
The Value Transformer can be initialized / registered by allocating an instance of it in MyDocument.m, but in the end, it doesn't matter that much as long as the transformer header is imported into the theEntity Header (see above). I have experimented with this and it does not remove the error I get below. For reference, there is earlier discussion on whether or not the value transformer needs to be registered (see the comments by CornPuff and Brian Webster).
Back to the problem at hand, a good code example from Apple is here which shows an alternative initialization of the value transformer, I tried that setup too.
Putting this into action, I have a method to load a test image and assign it to the transformable attribute in MyDocument.m (from a selected Entity instance in an NSTable):
- (IBAction)addImg:(id)sender {
NSImage *theImg = [[NSImage alloc] initWithContentsOfFile:#"/Desktop/testImage.jpg"];
//[theImageView setImage:theImg]; // as a test, this displays ok
// "theEntities" below are defined as an IBOutlet to my Array Controller:
[theEntities setValue:theImg forKeyPath:#"selection.theImageAtt"];
NSLog(#"after setting the image ..."); // this never logs
}
Zeroing-in on where the code breaks, the line below:
[theEntities setValue:theImg forKeyPath:#"selection.theImageAtt"];
gives the error:
Cannot create BOOL from object <4d4d002a 0003717e 8989898a 8a8a8b8b 8b8b8b8b
8a8a8a89 89898888 88878787 8a8a8a89 89898888 88888888 88888889 89898a8a
8a8a8a8a 8b8b8b88 88888787 87898989 8a8a8a89 89898a8a 8a8c8c8c 8b8b8b8a
8a8a8888 .... and so on for the size of the Image array ...
If I comment out the said line above then my NSTable populates just fine (so the bindings and array controller seem ok), but of course with no image in the NSImageView.
As a check, the conversion code used in the Image Transformer works as expected (this is tested separately from the value transformer):
// Image to Data Conversion:
NSImage *imageIn = [[NSImage alloc] initWithContentsOfFile:#"testImage.jpg"];
NSData *imgData = [imageIn TIFFRepresentation];
NSImage *imageOut = [[NSImage alloc] initWithData:imgData];
[theImageDisplay setImage:imageOut];
What am I missing on this?
I found that the error reported above, namely,
Cannot create BOOL from object ...
does not occur when using an Image Well or Image Cell (subclasses of NSImageView) rather than the Custom View that I was trying to write to earlier.
So for now I'm using the default value transformer rather than a custom value transformer. This is a workable solution, but academically speaking, it would be nice to know why the default Custom View led to errors when binding to a Core Data attribute (defined as transformable in the Core Date Model).
Digging into this a little further, the Image Well inherits from NSImageView, so at least one difference is that they are distinct with regard to the "editable" property (Image Wells are editable which plays well with Drag-n-Drop). So in an attempt to reconcile these two, I set my Custom View to editable, thinking that this might resolve the problem:
theImageView = [[NSImageView alloc] init];
[theImageView setEditable:YES];
But it must be something else, this does not resolve the error above. For now at least, I have a workable solution. Should others encounter this, I hope these notes are helpful!
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.
I've subclassed MKAnnotation so that i can assign objects to each annotation, this is so that when the rightCalloutAccessoryView is clicked i can push a navigation controller with the object passed to it and display the objects data in another view.
This all works great apart from one thing, i've extended upon initWithCoordinate like so:
-(id)initWithCoordinate:(CLLocationCoordinate2D)coord andObject:(NSManagedObject *)object {
[self setPlace:object];
coordinate = coord;
title = [place valueForKey:#"name"];
subtitle = [place valueForKey:#"address"];
return self;
}
Although everything is working great i'm recieving the warning:
NO '-initWithCoordinate:andObject:' method found
Which means i'm doing something wrong somewhere, what is the correct way to go about adding upon initWithCoorinate?
Put the prototype -(id)initWithCoordinate:(CLLocationCoordinate2D)coord andObject:(NSManagedObject *)object in .h file.
I am developing a pie chart application using Core-Plot on iPhone.
There are no issues in drawing the pie-chart. But I am unable to interact with its slices. I even tried interacting with them using touchesBegan event. But even this method is not getting invoked.
Please help me in this regards. User interaction has been enabled.
Try to implement <CPPiechartDelegate> in your class.
Then implement
-(void)pieChart:(CPPieChart *)plot sliceWasSelectedAtRecordIndex:(NSUInteger)index
{
// This will inform you the index of the slice that was touched or clicked.
}
Also use latest Coreplot framework as old framework has some issues related to selecting touch event.
Edit
Try This
In yourViewController.h file
#import <UIKit/UIKit.h>
#import "CorePlot-CocoaTouch.h"
#interface yourViewController : UIViewController <CPPieChartDataSource, CPPieChartDelegate>
{
}
In yourViewController.m file
-(void)pieChart:(CPPieChart *)plot sliceWasSelectedAtRecordIndex:(NSUInteger)index
{
NSString *selectedIndex = [NSString stringWithFormat:#"Selected index: %lu", index];
NSLog(#"You have selected index=%#",selectedIndex);
}
Finally view your gdb log to get the response ,which index you have selected.
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;
}