in the implementations class charged of displaying PINS, i have reserved two variables (title and sub title), in this example, only the word USA (the title) is displayed when i click on the PIN.
CLLocationCoordinate2D location2D = (CLLocationCoordinate2D){ .latitude = latitudeOfUserLocation, .longitude = longitudeOfUserLocation };
ManageAnnotations *annotation=[[ManageAnnotations alloc]initWithTitle:#"USA" adresseDuTheme:#"Colorado" coordinate:location2D];//only USA is displayed
annotation.pinColor = MKPinAnnotationColorRed; //or red or whatever
[self->mapView addAnnotation:annotation];
MKCoordinateSpan span={.latitudeDelta=1,.longitudeDelta=0.5};
MKCoordinateRegion region={location2D,span};
[mapView setRegion:region];
Although, in ManageAnnotations class, i have reserved two variables for the title and the subtitle.
#interface ManageAnnotations : NSObject<MKAnnotation>{
NSString *_libelle;
NSString *_adresse;
CLLocationCoordinate2D _coordinate;
}
//
#property(nonatomic,assign)MKPinAnnotationColor pinColor;
#property(copy)NSString *libelle;
#property(copy)NSString *adresse;
#property(nonatomic,readonly)CLLocationCoordinate2D coordinate;
-(id)initWithTitle:(NSString*)libelle adresseDuTheme:(NSString*)adresse coordinate:(CLLocationCoordinate2D)coordinate;
#end
#import "ManageAnnotations.h"
#implementation ManageAnnotations
#synthesize pinColor;
#synthesize libelle=_libelle;
#synthesize adresse=_adresse;
#synthesize coordinate=_coordinate;
-(id)initWithTitle:(NSString*)libelle adresseDuTheme:(NSString*)adresse coordinate:(CLLocationCoordinate2D)coordinate{
if((self=[super init])){
_libelle=[libelle copy];
_adresse=[adresse copy];
_coordinate=coordinate;
}
return self;
}
-(NSString*)title{
return _libelle;
}
-(NSString*)subTitle{
return _adresse;
}
#end
The MKAnnotation protocol defines the subtitle property as:
#property (nonatomic, readonly, copy) NSString *subtitle
Note subtitle is all lowercase but your class has subTitle (uppercase T) which the map view will not call.
Change the method declaration to:
-(NSString*)subtitle
change subTitle to subtitle in method declaration and property declaration and it will work.
:) happy coding,
Related
My Map shows a pin on a specific place but it's not showing the pin
Here is the code
WadiRumViewControllerJordan.h
#import <UIKit/UIKit.h>
#include <MapKit/MapKit.h>
#interface WadiRumViewControllerJordan : UIViewController
#property (strong, nonatomic) IBOutlet MKMapView *WadiRumMapView;
#end
WadiRumViewControllerJordan.m
#import "WadiRumViewControllerJordan.h"
#import "WadiRumNSOjectPIN.h"
#interface WadiRumViewControllerJordan ()
#end
//Wadi Rum Coordinates
#define WadiRum_Latitude 29.537355
#define WidiRum_longtitude 35.415026
//Wadi Rum Span
#define WadiRumSpan 0.01f;
#implementation WadiRumViewControllerJordan
#synthesize WadiRumMapView;
- (void)viewDidLoad {
[super viewDidLoad];
//Create WadiRum Region
MKCoordinateRegion WadiRumRegion;
//Center
CLLocationCoordinate2D center;
center.latitude = WadiRum_Latitude;
center.longitude = WidiRum_longtitude;
//Span
MKCoordinateSpan span;
span.latitudeDelta = WadiRum_Latitude;
span.longitudeDelta = WidiRum_longtitude;
WadiRumRegion.center = center;
WadiRumRegion.span = span;
//Set our map
[WadiRumMapView setRegion:WadiRumRegion animated:YES];
//WadiRumNSObjectPIN
//1. Create a coordinate for the use of WadiRum
CLLocationCoordinate2D WadiRumLocation;
WadiRumLocation.latitude = WadiRum_Latitude;
WadiRumLocation.longitude = WidiRum_longtitude;
WadiRumNSOjectPIN * WadiRumAnnitation = [[WadiRumNSOjectPIN alloc] init];
WadiRumAnnitation.coordinate = WadiRumLocation;
WadiRumAnnitation.title = #"Services";
WadiRumAnnitation.subtitle = #"Desert";
{[self.WadiRumMapView addAnnotation:WadiRumAnnitation];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be WadiRumNSOjectPIN
}
#end
WadiRumNSOjectPIN.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface WadiRumNSOjectPIN : NSObject <MKAnnotation>
#property(nonatomic, assign) CLLocationCoordinate2D coordinate;
#property(nonatomic, copy) NSString * title;
#property(nonatomic, copy) NSString * subtitle;
#end
WadiRumNSOjectPIN.m
#import "WadiRumNSOjectPIN.h"
#implementation WadiRumNSOjectPIN
#synthesize coordinate;
- (id)initWithLocation:(CLLocationCoordinate2D)coord {
self = [super init];
if (self) {
coordinate = coord;
}
return self;
}
#synthesize coordinate, title, subtitle;
#end
I edited the code above to make it exactly like what I want, I got this error in the picture bellow
In order to conform to MKAnnotation, you must have properties called coordinate, title and subtitle. You've added three extra properties, ttcoordinate, tttitle, and ttsubtitle, but MKAnnotation is going to ignore those, and will look for coordinate, title, and subtitle.
The key reason you're not seeing your annotation is that you're setting ttcoordinate in viewDidLoad. But MKAnnotation will not use that, but rather will refer to the coordinate property you synthesized, but never set. (You do have an initWithLocation method, which suggests you were going to update coordinate, but you never call that.)
Bottom line, I would suggest renaming ttcoordinate, ttitle and ttsubtitle to coordinate, title, and subtitle, and updating all of those references accordingly, and that should fix everything. And you can retire the #synthesize line.
Working in iOS 6, I cannot seem to get the map view to update itself to show markers added or removed.
When the app opens, a number of markers are placed on the map. The user can then make choices that add new markers and/or remove existing markers. This uses the same method as when the RMMarkerManager is populated at viewWillLoad, and I can iterate through the markers in the RMMarkerManager and see that it has the new set of markers, but the map view never updates to show them.
I have tried [mapview setNeedsDisplay] with no effect.
Clearly, I'm missing something that causes the mapview to update the display of the markers, but I have not figured out what, despite lots of head-scratching and digging through documentations and posts.
I will appreciate any suggestions as to what I should change or add.
As requested, here is the appropriate code. I'll explain how it works.
In the viewController's createMarkers method, the markers are created by accessing a sqlite database. One marker is created for each record that I want displayed as a marker on the map. I then iterate through the array of markers, adding each to the mapView's marketManager (addMarker method). The method createMarkers is called in the viewController's viewWillLoad method, and works correctly: all markers are displayed.
When using the app, a user can select or de-select records in the database. Then the viewController receives a notification that the user has changed the selection, and calls its setMarkers method. The mapview's marketmanager gets a removeMarkers message, then the marker array is re-created; it now has markers reflecting the user's selections. But the map never updates the on-view markers. Markers removed by the user are not removed on the view; markers added by the user are not added.
After the update, I can iterate through mapview.markermanager.markers and see that it now contains the new markers. But they're never shown on the mapView.
Class: Marker, subclass of RMMarker.
Simply holds data about the marker to be displayed
Marker.h:
// Marker.h
#import <Foundation/Foundation.h>
#import "Location.h"
#import "RMMarker.h"
#interface Marker : RMMarker {
NSString *category_name;
NSString *name;
NSString *desc;
NSString *address;
NSString *png;
int marker_id;
float lat;
float longi;
CLLocationCoordinate2D node;
float label_x_offset;
float label_y_offset;
}
#property (nonatomic, strong) NSString *category_name;
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *desc;
#property (nonatomic, retain) NSString *address;
#property (nonatomic, retain) NSString *png;
#property (nonatomic) int marker_id;
#property (nonatomic) float lat;
#property (nonatomic) float longi;
#property (nonatomic) CLLocationCoordinate2D node;
#property (nonatomic) float label_x_offset;
#property (nonatomic) float label_y_offset;
#end
Class: Markers
Holds an NSMutableArray of Markers, which is populated from a sqlite database:
// Markers.m
#import "Markers.h"
#import "defs.h"
#import "FileLocator.h"
#import "Marker.h"
#implementation Markers
#synthesize markers;
-(NSMutableArray *) createMarkers {
markers = [[NSMutableArray alloc] init];
[self openDatabase];
NSString *query = [NSString stringWithFormat:#"SELECT categories.selected, categories.category_id, categories.png, places.name, address, description, latitude, longitude, place_id FROM places, categories WHERE (categories.selected = 1 AND places.category_id = categories.category_id);"];
debugPrintArgs(#"query: %#", query);
FMResultSet *rs = [db executeQuery:query];
while ([rs next]) {
Marker *marker = [[Marker alloc] init];
marker.marker_id = [rs intForColumn:#"place_id"];
marker.name = [rs stringForColumn:#"name"];
marker.address = [rs stringForColumn:#"address"];
marker.desc = [rs stringForColumn:#"description"];
marker.lat = [rs doubleForColumn:#"latitude"];
marker.longi = [rs doubleForColumn:#"longitude"];
marker.png = [rs stringForColumn:#"png"];
debugPrintArgs(#"%#, %#, %#, %f, %f", marker.name, marker.address, marker.description, marker.lat, marker.longi);
marker.label_y_offset = 150.0f;
marker.label_x_offset = 30.0f;
[markers addObject:marker];
}
[db close];
return markers;
}
#end
Methods in the viewcontroller:
setMarkers:
Iterates through the NSMUtableArray Markers, calling the method addMarker: for each marker in that array:
- (void) setMarkers {
// class Markers is essentially an NSMutableArray that holds instantiations of Marker - one for each marker to be displayed
// Markers is also responsible for populating itself from a sqlite database via the createMarkers method
Markers *markers = [[Markers alloc] init];
NSMutableArray *allMarkers = [markers createMarkers];
// allMarkers contains the markers to be displayed.
CLLocationCoordinate2D loc;
if ([allMarkers count] > 0) {
for (Marker *mrkr in allMarkers) {
loc.longitude = mrkr.longi;
loc.latitude = mrkr.lat ;
[self addMarker: mrkr at:loc withText:mrkr.name xOffset: mrkr.label_x_offset yOffset: mrkr.label_y_offset png: mrkr.png];
}
}
}
Also in the viewController: addMarker
And, finally, the addMarker method used to add a marker to the RMMarkerManager:
- (void) addMarker: (Marker *) marker at:(CLLocationCoordinate2D)loc withText:(NSString *)text xOffset: (float) x_offset yOffset:(float) y_offset png:(NSString *) png {
UIImage* markerImage = [UIImage imageNamed:png];
[marker replaceUIImage:markerImage anchorPoint:CGPointMake(0.38f, 1.08f)];
[viewMap.markerManager addMarker: marker AtLatLong: loc];
CGPoint position = CGPointMake( 0.0f, 0.0f);
[marker changeLabelUsingText: text position: position ];
}
The offending line here is in setMarkers:
Markers *markers = [[Markers alloc] init];
Making markers an instance variable and doing the alloc-init in the class's viewWillLoad method corrected the problem.
I wish I could explain this better, but I am unsure as to why this caused the problem. But, it's corrected now and I'll explore when I've got some time.
I have this code below working perfectly for a unique Pin and one annotation. I want to adapt it without too many changes to display more Pins, locations and annotations.
There is a class called MyAnnotationPins with the following lines:
MyAnnotationPins.h
#interface MyAnnotationPins : NSObject < MKAnnotation>
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, readonly, copy) NSString *title;
#property (nonatomic, readonly, copy) NSString *subtitle;
-(id)initWithCoordinate:(CLLocationCoordinate2D)annotCoordinate title:(NSString*)annotTitle subtitle:(NSString*)annotSubtitle;
MyAnnotationPins.m
#synthesize coordinate;
#synthesize subtitle;
#synthesize title;
-(id)initWithCoordinate:(CLLocationCoordinate2D)annotCoordinate title:(NSString*)annotTitle subtitle:(NSString*)annotSubtitle
{
self = [super init];
if (self)
{
coordinate = annotCoordinate;
subtitle = [[NSString alloc] initWithString:annotSubtitle];
title = [[NSString alloc] initWithString:annotTitle];
}
return self;
}
And at the view controller the following code:
SecondViewController.h
import "MyAnnotationPins.h"
#interface SecondViewController : UIViewController < MKMapViewDelegate >
#property (weak, nonatomic) IBOutlet MKMapView *mapView;
#property (strong, nonatomic) MyAnnotationPins* biblioAnnotation;
At the SecondViewController.m the implementation:
- (void)viewDidLoad
{
[super viewDidLoad];
mapView.delegate = self;
MKCoordinateRegion mapRegion;
mapRegion.center.latitude=-18.924129;
mapRegion.center.longitude=-48.283963;
mapRegion.span.latitudeDelta=0.2;
mapRegion.span.longitudeDelta=0.2;
[mapView setRegion:mapRegion animated:YES];
///// This is just for One annotaion/Pin on Map /////
CLLocationCoordinate2D parliamentLocation = CLLocationCoordinate2DMake(-18.924129, -48.283963);
biblioAnnotation = [[MyAnnotationPins alloc]
initWithCoordinate:parliamentLocation
title:#"Ponto Biblioteca"
subtitle:#"Taxi proximo"];
[mapView addAnnotation:biblioAnnotation];
If you want more than one pin and annotaion copy the CLLocation instance below and change the following atributes
CLLocationCoordinate2D secondLocation = CLLocationCoordinate2DMake(another latitude, another longitude);
secondAnnotation = [[MyAnnotationPins alloc]
initWithCoordinate:secondLocation
title:#"Second Title"
subtitle:#"Second subtitle"];
[mapView addAnnotation:secondAnnotation]; <code>
And so on for the third, fourth fifth etc. Do not forget to create the secondLocation proerty at your view controller like the first one in SecondViewController.h and also
#synthesize secondAnnotation property at SecondViewController.m file
#property (strong, nonatomic) MyAnnotationPins* secondAnnotation;
Add them in a loop like so
for(X in Y)
{
CLLocationCoordinate2D parliamentLocation = CLLocationCoordinate2DMake(-18.924129, -48.283963);
MyAnnotationPins* biblioAnnotation = [[MyAnnotationPins alloc]
initWithCoordinate:parliamentLocation
title:#"Ponto Biblioteca"
subtitle:#"Taxi proximo"];
[mapView addAnnotation:biblioAnnotation];
}
As you loop through your list you can get the right coordinates and title for each one. This way you list can grow or shrink and you won't need to add third, fourth or fifth annotation properties.
I am trying to put MKAnnotations on MKMapView. I am following the procedure that has been suggested on various available tutorials. I have created PlaceMark.h/.m file from NSobject Class as following.
PlaceMark.h
#import <Foundation/Foundation.h>
#import <MapKit/MKAnnotation.h>
#interface PlaceMark : NSObject<MKAnnotation>
{
CLLocationCoordinate2D coordinate;
NSString *title;
}
#property (nonatomic,copy) NSString *title;
#property (nonatomic,readonly) CLLocationCoordinate2D coordinate;
#end
PlaceMark.m
#import "PlaceMark.h"
#implementation PlaceMark
#synthesize coordinate,title;
-(void)dealloc
{
[title release];
[super dealloc];
}
#end
in my viewController which holds MKMapView, in viewDidload I have following code
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
MKCoordinateRegion region;
region.center.latitude=37.3305262;
region.center.longitude=-122.0290935;
region.span.latitudeDelta=0.01;
region.span.longitudeDelta=0.01;
[parkingMap setRegion:region animated:YES];
PlaceMark *ann=[[[PlaceMark alloc]init]autorelease]; // I have also tired it using explicitly defining method -(id)initwithTitle.... but it did not worked.
ann.title=#"Test";
ann.coordinate= region.center;
[parkingMap addAnnotation:ann];
}
Can anyone please tell me what I am doing wrong? BTW I am using Xcode4.2 with iOS sdk5.0 and
Not using storyboards/automatic reference counting
Thanks
Sumit
You've defined the coordinate property as readonly so you can't set it.
Change it to readwrite.
Also, if you just need a basic annotation object with a title, subtitle, and settable coordinate, you can use the built-in MKPointAnnotation class instead of defining your own class. The creation and initialization would be identical (just replace your class name with MKPointAnnotation).
I'm building a MapKit based app for iPhone.
I have a number of MKPolylines added to the map.
However, instead of having a MKPolyline, I would like to have my own Model class conforming to the MKOverlay protocol added to the map so that I can access the model properties when creating the corresponding view in mapView:viewForOverlay.
The problem is that I can't find the way to inherit from MKPolyline because it doesn't have any init methods that I can call from the subclass' init. You can only create them using the convenience methods.
How can I bring together the model properties and the MKPolyline behaviour?
MANIAK_dobrii's code is the way to go but I found I had to implement some additional MKMultiPoint methods to get it to work, here are my complete header and implementation files for an AnchorLine class I used:-
Header AnchorLine.h
#import <MapKit/MapKit.h>
#interface AnchorLine : NSObject <MKOverlay> {
MKPolyline* polyline;
}
#property (nonatomic, retain) MKPolyline* polyline;
+ (AnchorLine*)initWithPolyline: (MKPolyline*) line;
#end
Implementation AnchorLine.m
#import "AnchorLine.h"
#implementation AnchorLine
#synthesize polyline;
+ (AnchorLine*)initWithPolyline: (MKPolyline*) line {
AnchorLine* anchorLine = [[AnchorLine alloc] init];
anchorLine.polyline = line;
return [anchorLine autorelease];
}
- (void) dealloc {
[polyline release];
polyline = nil;
[super dealloc];
}
#pragma mark MKOverlay
//#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (CLLocationCoordinate2D) coordinate {
return [polyline coordinate];
}
//#property (nonatomic, readonly) MKMapRect boundingMapRect;
- (MKMapRect) boundingMapRect {
return [polyline boundingMapRect];
}
- (BOOL)intersectsMapRect:(MKMapRect)mapRect {
return [polyline intersectsMapRect:mapRect];
}
- (MKMapPoint *) points {
return [polyline points];
}
-(NSUInteger) pointCount {
return [polyline pointCount];
}
- (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range {
return [polyline getCoordinates:coords range:range];
}
#end
Hope that helps someone.
You can set an associated object attribute of the class. This allows you to bind an instance variable to an existing class. Make sure you properly clean up after yourself.
It is true that MKPolyline doesn't have its own init method. In fact the only class in MKPolyline's inheritance chain that does have an init method is NSObject.
So when I subclassed MKPolyline I just overrode the init method defined by NSObject...
-(id) init {
self = [super init];
if(self) {
//my initialization here
}
return self;
}
Then when you want to instantiate your subclass with coordinates you might do something like this...
-MyPolyline* myPolyline = (MyPolyline*)[MyPolyline polylineWithCoordinates:coordinates count:coordinateCount];
UPDATE: There's another option (could be better) to use message forwarding for this (like -forwardingTargetForSelector or stuff).
I had the same issue today but came up with other solution. Instead of using suggested by Wayne associated object attribute stuff I just encapsulated MKPolyline in another class and transferred MKOverlay protocol's messages to it.
So I've got something like in .h:
#interface MyOverlay : NSObject <MKOverlay>
{
MKPolyline* polyline;
id object;
}
#property (nonatomic, retain) id object;
#property (nonatomic, retain) MKPolyline* polyline;
+ (MyOverlay*)myOverlayWithObject: (id)anObject;
#end
And in .m:
#implementation MyOverlay
#synthesize object;
#synthesize polyline;
+ (MyOverlay*)routePartOverlayWithObject: (id)anObject {
MyOverlay* myOverlay = [[MyOverlay alloc] init];
... generating MKPolyline ...
myOverlay.polyline = ... generated polyline ...;
routePartOverlay.object = anObject;
return [myOverlay autorelease];
}
- (void) dealloc {
[cdRoutePart release]; cdRoutePart = nil;
[polyline release]; polyline = nil;
[super dealloc];
}
#pragma mark MKOverlay
//#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (CLLocationCoordinate2D) coordinate {
return [polyline coordinate];
}
//#property (nonatomic, readonly) MKMapRect boundingMapRect;
- (MKMapRect) boundingMapRect {
return [polyline boundingMapRect];
}
- (BOOL)intersectsMapRect:(MKMapRect)mapRect {
return [polyline intersectsMapRect:mapRect];
}
#end
So MyOverlay behaves like MKPolyline (conforms to MKOverlay) and at the same time I can do anything with it, having as many properties as I need.
I rewrote jmathew's excellent answer which I didn't find anywhere else, and it works like charm. A little modified version in Swift:
final class CustomPolyline: MKPolyline {
private(set) var color: UIColor?
private(set) var width: CGFloat?
convenience init(coordinates: [CLLocationCoordinate2D], color: UIColor, width: CGFloat) {
self.init(coordinates: coordinates, count: coordinates.count)
self.color = color
self.width = width
}
}
Whats mentioned here so far hasn't quite worked for me but I managed a solution based on the other answers and some independent research. I am not 100% certain in this but you can cast a MKPolyline into a custom sub-class only if you use the static method call that calls the right 'init' method internally.
(CustomPolyline*)[CustomPolyline polylineWithCoordinates:coordinates count:coordinateCount]
The above won't work because polylineWithCoordinates only allocates memory for an MKPolyline object and not CustomPolyline. I suspect what's happening internally is that polylineWithCoordinates calls another initializer method in a manner similar to: [MKPolyline otherInitMethod:...]. And its not allocating the proper amount of memory because its now using an MKPolyline static method call and not our CustomPolyline static call.
However if we use
(CustomPolyline*)[CustomPolyline polylineWithPoints:polyline.points count:polyline.pointCount];
It does work. I think this is because polylineWithPoints is using an initializer that returns an id not just chaining to another method call. And since we called it using the CustomPolyline class the initializer allocates memory for CustomPolyline not MKPolyline.
I could be completely wrong on why it works. But I've tested this and it seems to work fine. MKPolygon can be extended in a similar manner. In that case I think the correct static method to use is MKPolygon polygonWithCoordinates:points count:pointSet.count]]
My implementation for reference:
CustomPolyline.h
#import <MapKit/MapKit.h>
typedef enum {
CustomPolylineTypeNone = 0,
CustomPolylineDifferentStrokes
} CustomPolylineType;
/**
* CustomPolyline wraps MKPolyline with additional information about a polyline useful for differentiation.
*/
#interface CustomPolyline : MKPolyline
#property CustomPolylineType type;
-(CustomPolyline*)initWithMKPolyline:(MKPolyline*)polyline;
#end
CustomPolyline.m
#import "CustomPolyline.h"
#implementation CustomPolyline
#synthesize type;
/**
* Takes an MKPolyline and uses its attributes to create a new CustomPolyline
*/
-(CustomPolyline*)initWithMKPolyline:(MKPolyline*)polyline
{
// We must use the this specific class function in this manner to generate an actual
// CustomPolyline object as opposed to a MKPolyline by a different name
return (CustomPolyline*)[CustomPolyline polylineWithPoints:polyline.points count:polyline.pointCount];
}
#end