I've been trying to do a simple app using MKMapView, and I'm getting hit with a SIGABRT error upon trying to call the <MKAnnotation> class.
The DetailViewController.m file:
#import "WVTDetailViewController.h"
#import <MapKit/MapKit.h>
#import <AddressBook/AddressBook.h>
#import "Pin.h"
#interface WVTDetailViewController ()
{
CLLocationCoordinate2D location;
NSMutableDictionary *masterDict;
NSDictionary *googleDict;
NSDictionary *yahooDict;
NSDictionary *appleDict;
NSDictionary *microsoftDict;
NSDictionary *facebookDict;
}
- (void)configureView;
#end
#implementation WVTDetailViewController
#synthesize address = _address;
- (void)configureView
{
// Update the user interface for the detail item.
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self configureView];
[_mapView setShowsUserLocation:YES];
CLLocationCoordinate2D googleLocation;
googleLocation.latitude = 37.4221;
googleLocation.longitude = -122.0844;
// Yahoo:
CLLocationCoordinate2D yahooLocation;
yahooLocation.latitude = 37.417354;
yahooLocation.longitude = -122.025189;
// Apple:
CLLocationCoordinate2D appleLocation;
appleLocation.latitude = 37.332313;
appleLocation.longitude = -122.030746;
// Microsoft:
CLLocationCoordinate2D microsoftLocation;
microsoftLocation.latitude = 47.639764;
microsoftLocation.longitude = -122.128435;
// Facebook:
CLLocationCoordinate2D facebookLocation;
facebookLocation.latitude = 37.483489;
facebookLocation.longitude = -122.149542;
if ([_address isEqualToString:#"Google"])
{
location = googleLocation;
}
if ([_address isEqualToString:#"Yahoo"])
{
location = yahooLocation;
}
if ([_address isEqualToString:#"Apple"])
{
location = appleLocation;
}
if ([_address isEqualToString:#"Microsoft"])
{
location = microsoftLocation;
}
if ([_address isEqualToString:#"Facebook"])
{
location = facebookLocation;
}
googleDict = #{(NSString *)kABPersonAddressStreetKey: #"1600 Amphitheatre Pkwy",
(NSString *)kABPersonAddressCityKey: #"Mountain View",
(NSString *)kABPersonAddressStateKey: #"CA",
(NSString *)kABPersonAddressZIPKey: #"94043"
};
yahooDict = #{(NSString *)kABPersonAddressStreetKey: #"701 1st Ave",
(NSString *)kABPersonAddressCityKey: #"Sunnyvale",
(NSString *)kABPersonAddressStateKey: #"CA",
(NSString *)kABPersonAddressZIPKey: #"94089"
};
appleDict = #{(NSString *)kABPersonAddressStreetKey: #"1 Infinite Loop",
(NSString *)kABPersonAddressCityKey: #"Cupertino",
(NSString *)kABPersonAddressStateKey: #"CA",
(NSString *)kABPersonAddressZIPKey: #"95014"
};
microsoftDict = #{(NSString *)kABPersonAddressStreetKey: #"One Microsoft Way",
(NSString *)kABPersonAddressCityKey: #"Redmond",
(NSString *)kABPersonAddressStateKey: #"WA",
(NSString *)kABPersonAddressZIPKey: #"98052"
};
facebookDict = #{(NSString *)kABPersonAddressStreetKey: #"1 Hacker Way",
(NSString *)kABPersonAddressCityKey: #"Menlo Park",
(NSString *)kABPersonAddressStateKey: #"CA",
(NSString *)kABPersonAddressZIPKey: #"94025"
};
masterDict = [[NSMutableDictionary alloc] init];
[masterDict setObject: googleDict forKey: #"Google"];
[masterDict setObject: yahooDict forKey: #"Yahoo"];
[masterDict setObject: appleDict forKey: #"Apple"];
[masterDict setObject: microsoftDict forKey: #"Microsoft"];
[masterDict setObject: facebookDict forKey: #"Facebook"];
}
- (void)viewWillAppear:(BOOL)animated
{
// Establish a 1.5km "square" around the 2D coordinate "location" and display it
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(location, 1500, 1500);
[_mapView setRegion:viewRegion animated:YES];
Pin *myLocationPin = [[Pin alloc] initWithNameAndCoords: _address coords: location];
myLocationPin.addr = [masterDict objectForKey:_address];
[_mapView addAnnotation: myLocationPin];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
and the Pin.m file (the one implementing <MKAnnoation>):
#import "Pin.h"
#import <AddressBook/AddressBook.h>
#implementation Pin
- (id) initWithNameAndCoords: (NSString *) name coords: (CLLocationCoordinate2D) coords
{
if (self = [super init])
{
_name = name;
_coords = coords;
}
return self;
}
- (NSString *)title
{
return _name;
}
- (MKMapItem*) returnMapItem
{
MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:[[MKPlacemark alloc] initWithCoordinate:_coords addressDictionary: _addr]];
mapItem.name = _name;
return mapItem;
}
#end
First off, the SIGABRT error is situated at the [_mapView addAnnotation: myLocationPin]; line. I'm also getting a warning in Pin.m saying that Auto property synthesis will not synthesize property declared in a protocol. I used the #synthesize directive to force synthesis.
I've looked through the debugger output, though, and that doesn't seem to be an issue - the synthesized properties are receiving values as they should be.
You should add this to getter function in Pin.m
-(CLLocationCoordinate2D)coordinate { return _coord; }
Related
I am developing an app using Google Map iOS SDK (Objective C).
Done so far:
Loaded markers from the server in map view
Generate random clusters only.
When I run the project it looks like this.
Screen shot of map view
It shows both markers and clusters both in the map view at zoom level 10. But I wanted to show clusters first then when I zoomed in it should show the real markers. Not the randomly generated markers that I created because I don't know a way to show the clusters in the map.
Here is the full code with fake URL link:
#import "ViewController.h"
//#import "CSMarker.h"
#import <GoogleMaps/GoogleMaps.h>
#import <Google-Maps-iOS-Utils/GMUMarkerClustering.h>
//importing POI Item object - points of interest
#interface POIItem : NSObject<GMUClusterItem>
#property(nonatomic, readonly) CLLocationCoordinate2D position;
#property(nonatomic, readonly) NSString *name;
- (instancetype)initWithPosition:(CLLocationCoordinate2D)position name:(NSString *)name;
#end
#implementation POIItem
- (instancetype)initWithPosition:(CLLocationCoordinate2D)position name:(NSString *)name {
if ((self = [super init])) {
_position = position;
_name = [name copy];
}
return self;
}
#end
//implementation start - map view controller
static const NSUInteger kClusterItemCount = 60;
static const double kCameraLatitude = 25.277683999999997;
static const double kCameraLongitude = 55.309802999999995;
#interface ViewController ()<GMUClusterManagerDelegate, GMSMapViewDelegate>
{
NSMutableArray *waypoints_;
NSMutableArray *waypointStrings_;
GMSMapView *_mapView;
GMUClusterManager *_clusterManager;
}
#property(strong, nonatomic) NSURLSession *markerSession;
#property(strong, nonatomic) GMSMapView *mapView;
#property(copy, nonatomic) NSSet *markers;
#property(nonatomic, strong) NSMutableArray *markersArray;
#end
#implementation ViewController
#synthesize gs;
- (void)viewDidLoad {
[super viewDidLoad];
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:kCameraLatitude
longitude:kCameraLongitude
zoom:10];
self.mapView =
[GMSMapView mapWithFrame:self.view.bounds camera:camera];
[self.view addSubview:self.mapView];
self.mapView.settings.compassButton = YES;
self.mapView.settings.myLocationButton = YES;
//setup the cluster manager
NSURLSessionConfiguration *config =
[NSURLSessionConfiguration defaultSessionConfiguration];
config.URLCache = [[NSURLCache alloc] initWithMemoryCapacity:2 * 1024 * 1024
diskCapacity:10 * 1024 * 1024
diskPath:#"MarkerData"];
self.markerSession = [NSURLSession sessionWithConfiguration:config];
[self downloadMarkerData];
//cluster load setup
id<GMUClusterAlgorithm> algorithm = [[GMUNonHierarchicalDistanceBasedAlgorithm alloc] init];
id<GMUClusterIconGenerator> iconGenerator = [[GMUDefaultClusterIconGenerator alloc] init];
id<GMUClusterRenderer> renderer =
[[GMUDefaultClusterRenderer alloc] initWithMapView:self.mapView
clusterIconGenerator:iconGenerator];
_clusterManager =
[[GMUClusterManager alloc] initWithMap:self.mapView algorithm:algorithm renderer:renderer];
// Generate and add random items to the cluster manager.
[self generateClusterItems];
// Call cluster() after items have been added to perform the clustering and rendering on map.
[_clusterManager cluster];
// Register self to listen to both GMUClusterManagerDelegate and GMSMapViewDelegate events.
[_clusterManager setDelegate:self mapDelegate:self];
// Do any additional setup after loading the view, typically from a nib.
}
- (NSMutableArray *)markersArray
{
if (!_markersArray) {
_markersArray = [NSMutableArray array];
}
return _markersArray;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//downloading marker data
- (void)downloadMarkerData {
NSURL *lakesURL =
[NSURL URLWithString:#"http://myscrap.com/xxx.php/webservice/xxx/xxxx"];
NSURLSessionDataTask *task = [self.markerSession dataTaskWithURL:lakesURL
completionHandler:^(NSData *data, NSURLResponse *response, NSError *e)
{
NSArray *json = [NSJSONSerialization JSONObjectWithData:data
options:0
error:nil];
NSLog(#"json: %#",json);
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self createMarkerObjectsWithJson:json];
}];
}];
[task resume];
}
-(void)drawMarkers
{
for(GMSMarker *marker in self.markers) {
if(marker.map == nil) {
marker.map = self.mapView;
}
}
}
-(void)createMarkerObjectsWithJson:(NSArray *)json
{
NSMutableSet *mutableSet = [[NSMutableSet alloc] initWithSet:self.markers];
for (NSDictionary *markerData in json) {
GMSMarker *newMarker = [[GMSMarker alloc] init];
// newMarker.appearAnimation = [markerData[#"id"] integerValue];
newMarker.position = CLLocationCoordinate2DMake([markerData[#"latitud"] doubleValue],
[markerData[#"longitude"] doubleValue]);
newMarker.title = markerData[#"name"];
newMarker.snippet = markerData[#"adress"];
// [mutableSet addObject:newMarker];
newMarker.map=self.mapView;
}
self.markers =[mutableSet copy];
[self drawMarkers];
}
#pragma mark GMUClusterManagerDelegate
- (void)clusterManager:(GMUClusterManager *)clusterManager didTapCluster:(id<GMUCluster>)cluster {
GMSCameraPosition *newCamera =
[GMSCameraPosition cameraWithTarget:cluster.position zoom:_mapView.camera.zoom + 1];
GMSCameraUpdate *update = [GMSCameraUpdate setCamera:newCamera];
[_mapView moveCamera:update];
}
#pragma mark GMSMapViewDelegate
- (BOOL)mapView:(GMSMapView *)mapView didTapMarker:(GMSMarker *)marker {
POIItem *poiItem = marker.userData;
if (poiItem != nil) {
NSLog(#"Did tap marker for cluster item %#", poiItem.name);
} else {
NSLog(#"Did tap a normal marker");
}
return NO;
}
#pragma mark Private
// Randomly generates cluster items within some extent of the camera and adds them to the
// cluster manager.
- (void)generateClusterItems {
const double extent = 0.2;
for (int index = 1; index <= kClusterItemCount; ++index) {
double lat = kCameraLatitude + extent * [self randomScale];
double lng = kCameraLongitude + extent * [self randomScale];
NSString *name = [NSString stringWithFormat:#"Item %d", index];
id<GMUClusterItem> item =
[[POIItem alloc] initWithPosition:CLLocationCoordinate2DMake(lat, lng) name:name];
[_clusterManager addItem:item];
}
}
// Returns a random value between -1.0 and 1.0.
- (double)randomScale {
return (double)arc4random() / UINT32_MAX * 2.0 - 1.0;
}
try changing in view did load
GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:kCameraLatitude longitude:kCameraLongitude zoom:4];
the zoom to a 5 or 4. It then should show the clusters and not the markers. When you zoom in, it will show the "real" markers.
I am trying to create a custom annotation for a simple map view of mine, but the annotaiton pin is not showing up on the map when i run the simulator.
I have a MapViewAnnotation class which implements the MKAnnotation protocol, and I have another MapViewController class where I programmatically create a mapView and display the map.
MapViewAnnotation.h :
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MapViewAnnotation : NSObject <MKAnnotation>
#property (nonatomic, copy) NSString *title;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
-(id) initWithTitle: (NSString *) title AndCoordinate: (CLLocationCoordinate2D) coordinate;
#end
MapViewAnnotation.m :
#import "MapViewAnnotation.h"
#implementation MapViewAnnotation
#synthesize coordinate = _coordinate;
#synthesize title = _title;
-(id) initWithTitle:(NSString *)title AndCoordinate:(CLLocationCoordinate2D)coordinate{
self = [super init];
_title = title;
_coordinate = coordinate;
return self;
}
#end
MapViewController.h :
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface MapViewController : UIViewController
#property (weak, nonatomic) MKMapView *mapView;
-(void) goToLocation;
#end
MapViewController.m :
#import "MapViewController.h"
#import "MapViewAnnotation.h"
#define LATITUDE 42.3889;
#define LONGITUDE -72.5278;
#interface MapViewController ()
#end
#implementation MapViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.mapView = [[MKMapView alloc] initWithFrame:self.view.frame];
[self.view addSubview:self.mapView];
[self goToLocation];
[self.mapView addAnnotations:[self createAnnotations]];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void) goToLocation {
MKCoordinateRegion newRegion;
newRegion.center.latitude = LATITUDE;
newRegion.center.longitude = LONGITUDE;
newRegion.span.latitudeDelta = 0.05f;
newRegion.span.longitudeDelta = 0.05f;
self.mapView.showsPointsOfInterest = YES;
[self.mapView setRegion:newRegion animated:YES];
}
-(NSMutableArray *) createAnnotations {
NSMutableArray *annotations = [[NSMutableArray alloc] init];
NSString *path = [[NSBundle mainBundle] pathForResource:#"location" ofType:#"plist"];
NSArray *locations = [NSArray arrayWithContentsOfFile:path];
for (NSDictionary *row in locations){
NSNumber *latitude = [row objectForKey:#"latitude"];
NSNumber *longitude = [row objectForKey:#"longitude"];
NSString *title = [row objectForKey:#"title"];
CLLocationCoordinate2D coord;
coord.latitude = latitude.doubleValue;
coord.longitude = longitude.doubleValue;
MapViewAnnotation *annotation = [[MapViewAnnotation alloc] initWithTitle:title AndCoordinate:coord];
[annotations addObject: annotation];
}
return annotations;
}
#end
I am taking am going through a plist where I have coordinates for one test location that I am trying to show on my map, but the pin is not displaying on the map. Again, I am doing this all programmatically, no xib, or storyboards (although that should not be the problem).
I do not know why the annotation is not showing up. I tried looking on stack for some other situations like mine but was unlucky to find anything that could help.
Thank you.
Answer was pretty simple,
In my plist I had the coordinates
latitude: 42.37
longitude: 72.536
when it should of actually been -72.536 for longitude.
I have this code:
// MapViewController.h
#import <Foundation/Foundation.h>
#import "MyCLController.h"
#import <MapKit/MapKit.h>
#class ShowsContainerController;
#interface MapViewController : UIViewController <MyCLControllerDelegate,MKMapViewDelegate> {
MyCLController *locationController;
MKMapView *mapView;
}
- (void)locationUpdate:(CLLocation *)location;
- (void)locationError:(NSError *)error;
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
#property (assign) BOOL updateMap;
#property (strong) ShowsContainerController *parent;
#end
// MapViewController.m
#import "MapViewController.h"
#import "ShowsContainerController.h"
#define METERS_PER_MILE 1609.344
#implementation MapViewController
#synthesize parent, mapView;
- (void)viewDidLoad
{
[super viewDidLoad];
self.updateMap = YES;
locationController = [[MyCLController alloc] init];
locationController.delegate = self;
[locationController.locationManager startUpdatingLocation];
mapView.delegate = self;
}
- (void)locationUpdate:(CLLocation *)location {
if( self.updateMap ){
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, 0.5*METERS_PER_MILE, 0.5*METERS_PER_MILE);
[mapView setRegion:viewRegion animated:YES];
self.parent = (ShowsContainerController *)self.parentViewController;
[self.parent setLocation:location];
self.updateMap = NO;
}
}
- (void)locationError:(NSError *)error {
NSLog([error localizedDescription]);
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
self.parent.mapReady = YES;
if( self.parent.locationSet ){
[self.parent callApi];
}
}
#pragma mark MKMapViewDelegate
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation
{
NSLog(#"delegating user location");
// if it's the user location, just return nil.
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
NSLog(#"delegating pins");
static NSString* ShowAnnotationIdentifier = #"showAnnotationIdentifier";
MKPinAnnotationView* pinView = (MKPinAnnotationView *)
[mapView dequeueReusableAnnotationViewWithIdentifier:ShowAnnotationIdentifier];
if (!pinView)
{
// if an existing pin view was not available, create one
MKPinAnnotationView* customPinView = [[MKPinAnnotationView alloc]
initWithAnnotation:annotation reuseIdentifier:ShowAnnotationIdentifier];
customPinView.enabled = YES;
customPinView.pinColor = MKPinAnnotationColorRed;
customPinView.animatesDrop = YES;
customPinView.canShowCallout = YES;
return customPinView;
}
else
{
pinView.annotation = annotation;
}
return pinView;
}
#pragma mark -
#end
And here's the code adding my pins; it isn't fired until an api call is made, which is after viewDidAppear is fired:
- (void)placeAnnotations:(NSArray *)shows
{
NSEnumerator *e = [shows objectEnumerator];
id show;
NSMutableArray *annotations = [[NSMutableArray alloc] init];
while (show = [e nextObject]) {
double lat = [[[[[show objectForKey:#"venue"] objectForKey:#"location"] objectForKey:#"geo:point"] objectForKey:#"geo:lat"] doubleValue];
double lng = [[[[[show objectForKey:#"venue"] objectForKey:#"location"] objectForKey:#"geo:point"] objectForKey:#"geo:long"] doubleValue];
ShowAnnotation *annotation = [[ShowAnnotation alloc]
initWithCoordinate:CLLocationCoordinate2DMake(lat, lng)
withMap:self.mapController.mapView
withTitle:[show objectForKey:#"title"]
withSubtitle:[[show objectForKey:#"venue"] objectForKey:#"name" ]];
[annotations addObject:annotation];
}
NSLog(#"adding annotations");
[self.mapController.mapView addAnnotations:annotations];
}
The log shows "delegating user location", but never "delegating pins". It doesn't even fire viewForAnnotation more than the one initial time for the user's location.
The delegation seems to be working, otherwise I wouldn't be able to capture this selector, but why is it firing for only the user location and not firing for subsequent annotation additions?
I am trying to load my Map which I created with a kml File in Google-Maps. The Google Maps Link. I have to say it is only an example, but it is the same principle.
The easiest way is to load in a WebView, but that is ugly in my eyes.
Thank you for reading my Question!
Best regards CTS
To load a KML into a MKMapView:
Add the necessary frameworks (MapKit.framework and CoreLocation.framework) to your target;
Load and parse the KML;
Create your annotations on the basis of the KML; and
Set your map's region to encompass the annotations.
Thus, that might look like:
#import <MapKit/MapKit.h>
- (void)loadKml:(NSURL *)url
{
// parse the kml
Parser *parser = [[Parser alloc] initWithContentsOfURL:url];
parser.rowElementName = #"Placemark";
parser.elementNames = #[#"name", #"Snippet", #"coordinates", #"description"];
parser.attributeNames = nil;
[parser parse];
// add annotations for each of the entries
for (NSDictionary *locationDetails in parser.items)
{
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
annotation.title = locationDetails[#"name"];
annotation.subtitle = locationDetails[#"Snippet"];
NSArray *coordinates = [locationDetails[#"coordinates"] componentsSeparatedByString:#","];
annotation.coordinate = CLLocationCoordinate2DMake([coordinates[1] floatValue], [coordinates[0] floatValue]);
[self.mapView addAnnotation:annotation];
}
// update the map to focus on the region that encompasses all of your annotations
MKCoordinateRegion region;
if ([self.mapView.annotations count] > 1)
{
region = [self regionForAnnotations:self.mapView.annotations];
region = MKCoordinateRegionMake(region.center, MKCoordinateSpanMake(region.span.latitudeDelta * 1.05, region.span.longitudeDelta * 1.05)); // expand the region by 5%
}
else
{
id<MKAnnotation> annotation = self.mapView.annotations[0];
region = MKCoordinateRegionMakeWithDistance(annotation.coordinate, 100.0, 100.0);
}
[self.mapView setRegion:region animated:YES];
}
My Parser class is just a NSXMLParser subclass that I've written that will create an array of items one per occurrence of rowElementName, and for each row, it will grab the elements listed in the elementNames array.
Parser.h:
#import <Foundation/Foundation.h>
#interface Parser : NSXMLParser
#property (nonatomic, strong) NSString *rowElementName; // this is the element name that identifies a new row of data in the XML
#property (nonatomic, strong) NSArray *attributeNames; // this is the array of attributes we might want to retrieve for that element name
#property (nonatomic, strong) NSArray *elementNames; // this is the list of sub element names for which we're retrieving values
#property (nonatomic, strong) NSMutableArray *items; // after parsing, this is the array of parsed items
#end
Parser.m:
#import "Parser.h"
#interface Parser () <NSXMLParserDelegate>
#property (nonatomic, strong) NSMutableDictionary *item; // while parsing, this is the item currently being parsed
#property (nonatomic, strong) NSMutableString *elementValue; // this is the element within that item being parsed
#end
#implementation Parser
- (id)initWithContentsOfURL:(NSURL *)url
{
self = [super initWithContentsOfURL:url];
if (self)
{
self.delegate = self;
}
return self;
}
- (id)initWithData:(NSData *)data
{
self = [super initWithData:data];
if (self)
{
self.delegate = self;
}
return self;
}
- (id)initWithStream:(NSInputStream *)stream
{
self = [super initWithStream:stream];
if (self)
{
self.delegate = self;
}
return self;
}
#pragma mark - NSXMLParserDelegate methods
- (void)parserDidStartDocument:(NSXMLParser *)parser
{
self.items = [[NSMutableArray alloc] init];
if (!self.rowElementName)
NSLog(#"%s Warning: Failed to specify row identifier element name", __FUNCTION__);
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:self.rowElementName])
{
self.item = [[NSMutableDictionary alloc] init];
for (NSString *attributeName in self.attributeNames)
{
id attributeValue = [attributeDict valueForKey:attributeName];
if (attributeValue)
[self.item setObject:attributeValue forKey:attributeName];
}
}
else if ([self.elementNames containsObject:elementName])
{
self.elementValue = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
if (self.elementValue)
{
[self.elementValue appendString:string];
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:self.rowElementName])
{
[self.items addObject:self.item];
self.item = nil;
}
else if ([self.elementNames containsObject:elementName])
{
[self.item setValue:self.elementValue forKey:elementName];
self.elementValue = nil;
}
}
#end
Finally, the only other utility method that my loadKml uses is regionForAnnotations, which defines a region based upon a series of annotations. Rob Mooney wrote a simple routine to do that:
- (MKCoordinateRegion)regionForAnnotations:(NSArray *)annotations {
CLLocationDegrees minLat = 90.0;
CLLocationDegrees maxLat = -90.0;
CLLocationDegrees minLon = 180.0;
CLLocationDegrees maxLon = -180.0;
for (id <MKAnnotation> annotation in annotations) {
if (annotation.coordinate.latitude < minLat) {
minLat = annotation.coordinate.latitude;
}
if (annotation.coordinate.longitude < minLon) {
minLon = annotation.coordinate.longitude;
}
if (annotation.coordinate.latitude > maxLat) {
maxLat = annotation.coordinate.latitude;
}
if (annotation.coordinate.longitude > maxLon) {
maxLon = annotation.coordinate.longitude;
}
}
MKCoordinateSpan span = MKCoordinateSpanMake(maxLat - minLat, maxLon - minLon);
CLLocationCoordinate2D center = CLLocationCoordinate2DMake((maxLat - span.latitudeDelta / 2), maxLon - span.longitudeDelta / 2);
return MKCoordinateRegionMake(center, span);
}
I have searched but i didn't found any solution to my problem. I want to change already added pin. And here is my code. I'm getting "Code Here" in console.
I have in .h
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView;
myViewController.m
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView
{
userPins *myAnnot = (userPins *)annotationView.annotation;
[myAnnot setTitle:#"o21"];
NSLog(#"Code here!");
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"userPins";
if ([annotation isKindOfClass:[userPins class]]) {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
annotationView.image = [UIImage imageNamed:#"mapMarker-blue.png"];
annotationView.calloutOffset = CGPointMake(0,0);
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.rightCalloutAccessoryView = detailButton;
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
return annotationView;
}
return nil;
}
In userPins.m i have. But i didn't do this with setTitle too.
- (void)setTitle:(NSString *)title {
if (_subtitle != subtitle) {
[_subtitle release];
_subtitle = [subtitle retain];
}
}
- (NSString *)title{
return #"okan";
}
Edit: #AnnaKarenina
I have tried all. But setTitle is not running. I'm going crazy! This is all of my code. And I'm not getting errors.
userpins.m
#import "userPins.h"
#implementation userPins
#synthesize fbid = _fbid;
#synthesize coordinate = _coordinate;
#synthesize title = _title;
#synthesize subtitle = _subtitle;
- (id)initWithfbid:(NSInteger*)fbid
coordinate:(CLLocationCoordinate2D)coordinate {
if ((self = [super init])) {
_fbid = fbid;
_coordinate = coordinate;
}
return self;
}
- (NSString *)subtitle{
return #"Anything";
}
- (NSString *)title{
return #"Something";
}
- (void)dealloc
{
[_title release];
[_subtitle release];
[super dealloc];
}
#end
userPins.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface userPins : NSObject <MKAnnotation> {
NSInteger *_fbid;
CLLocationCoordinate2D _coordinate;
NSString *_title;
NSString *_subtitle;
}
#property (nonatomic, readonly) NSInteger *fbid;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
- (id)initWithfbid:(NSInteger*)fbid coordinate:(CLLocationCoordinate2D)coordinate;
#end
viewController.m
#import "lociseViewController.h"
#import "ASIHTTPRequest.h"
#import "SBJSON.h"
#import "userPins.h"
#implementation lociseViewController
#synthesize mapView = _mapView;
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
// Add new method above refreshTapped
- (void)plotPositions:(NSString *)responseString {
for (id<MKAnnotation> annotation in _mapView.annotations) {
[_mapView removeAnnotation:annotation];
}
NSDictionary * root = [responseString JSONValue];
NSArray *data = [root objectForKey:#"data"];
for (NSArray * row in data) {
NSInteger * fbid = (NSInteger *)[row objectAtIndex:0];
NSNumber * latitude = [row objectAtIndex:1];
NSNumber * longitude = [row objectAtIndex:2];
CLLocationCoordinate2D coordinate;
coordinate.latitude = latitude.doubleValue;
coordinate.longitude = longitude.doubleValue;
userPins *annotation = [[[userPins alloc] initWithfbid:fbid coordinate:coordinate] autorelease];
[_mapView addAnnotation:annotation];
}
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
// 1
MKCoordinateRegion mapRegion = [_mapView region];
CLLocationCoordinate2D centerLocation = mapRegion.center;
// 2
NSString *jsonFile = [[NSBundle mainBundle] pathForResource:#"command" ofType:#"json"];
NSString *formatString = [NSString stringWithContentsOfFile:jsonFile encoding:NSUTF8StringEncoding error:nil];
NSString *json = [NSString stringWithFormat:formatString, centerLocation.latitude, centerLocation.longitude, 0.5*METERS_PER_MILE];
NSURL *url = [NSURL URLWithString:#"http://localhost/users.json"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
request.requestMethod = #"POST";
//request.defaultResponseEncoding = NSISOLatin1StringEncoding;
//[request addRequestHeader:#"Content-Encoding" value:#"windows-1255"];
[request addRequestHeader:#"Content-Type" value:#"application/json"];
[request appendPostData:[json dataUsingEncoding:NSUTF8StringEncoding]];
[request setDefaultResponseEncoding:NSUTF8StringEncoding];
// 5
[request setDelegate:self];
[request setCompletionBlock:^{
NSString *responseString = [request responseString];
NSLog(#"Response: %#", responseString);
[self plotPositions:responseString];
}];
[request setFailedBlock:^{
NSError *error = [request error];
NSLog(#"Error: %#", error.localizedDescription);
}];
// 6
[request startAsynchronous];
//CLLocationManager *locationManager;
//locationManager.delegate = self;
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
// 1
CLLocationCoordinate2D zoomLocation;
zoomLocation.latitude = 35.333070;
zoomLocation.longitude = 33.302875;
// 2
MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 50*METERS_PER_MILE, 3*METERS_PER_MILE);
// 3
MKCoordinateRegion adjustedRegion = [_mapView regionThatFits:viewRegion];
// 4
[_mapView setRegion:adjustedRegion animated:YES];
}
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView
{
userPins *myAnnot = (userPins *)annotationView.annotation;
[myAnnot setTitle:#"o21"];
NSLog(#"Title: %#", annotationView.annotation.subtitle);
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
static NSString *identifier = #"userPins";
if ([annotation isKindOfClass:[userPins class]]) {
MKPinAnnotationView *annotationView = (MKPinAnnotationView *) [_mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil) {
annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
} else {
annotationView.annotation = annotation;
}
annotationView.image = [UIImage imageNamed:#"mapMarker-blue.png"];
annotationView.calloutOffset = CGPointMake(0,0);
UIButton *detailButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.rightCalloutAccessoryView = detailButton;
annotationView.enabled = YES;
annotationView.canShowCallout = YES;
return annotationView;
}
return nil;
}
- (void)viewDidUnload
{
[self setMapView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (void)dealloc {
[_mapView release];
[super dealloc];
}
#end
viewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#define METERS_PER_MILE 1609.344
#interface lociseViewController : UIViewController <MKMapViewDelegate> {
MKMapView *_mapView;
}
#property (nonatomic, retain) IBOutlet MKMapView *mapView;
- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)annotationView;
#end
The setTitle: and title methods in userPins.m don't look right.
The setTitle: method is looking at and setting subtitle instead of title. The title method returns a constant string so even if setTitle: set the title ivar, the annotation will return the same constant string.
You could fix those methods or just declare title and subtitle as copy properties and #synthesize them in userPins.m (and release in dealloc). Then you don't need those methods at all (they will be implemented automatically).
By the way, when you add the annotation, be sure to set the title to something (even a single space) otherwise the callout will not display and didSelectAnnotationView will not be called.
A separate problem is a memory leak in viewForAnnotation. You should add an autorelease where you alloc+init annotationView.
Regarding The Updated Code:
In userPins.m, it still returns constant strings for subtitle and title thus hiding the changed ivars. Change it to:
- (id)initWithfbid:(NSInteger*)fbid
coordinate:(CLLocationCoordinate2D)coordinate {
if ((self = [super init])) {
_fbid = fbid;
_coordinate = coordinate;
//init the ivars here...
_title = #"Something";
_subtitle = #"Anything";
}
return self;
}
/* comment these methods out
- (NSString *)subtitle{
return #"Anything";
}
- (NSString *)title{
return #"Something";
}
*/
A new, unrelated problem is that fbid should probably be NSNumber * instead of NSInteger *. NSInteger is not an object and an NSNumber is what the JSON parser is most likely giving you.