I've been trying to get rid of memory leaks in mapview. I am using custom map pin class.
Everything works, but problem is - I need to filter mapview results. when i remove all mapview annotations - and add filtered results - performance tool finds leaks. but in this mapPin class I am using are used autorelease, so they should be released, but they aren't. what am I doing wrong?
MapPin.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#import <MapKit/MKMapView.h>
#import <MapKit/MKAnnotation.h>
#interface MapPin : NSObject<MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString * picture;
NSInteger tag_number;
}
#property (nonatomic,assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
- (id) initWithCoordinate:(CLLocationCoordinate2D) coord;
- (id) initWithCoordinate:(CLLocationCoordinate2D) coord title:(NSString *) title;
- (id) initWithCoordinate:(CLLocationCoordinate2D) coord title:(NSString *) title subtitle:(NSString *) subtitle;
- (id) initWithCoordinate:(CLLocationCoordinate2D) coord title:(NSString *) title subtitle:(NSString *) subtitle image:(NSString *) pic;
- (id) initWithCoordinate:(CLLocationCoordinate2D) coord title:(NSString *) title subtitle:(NSString *) subtitle image:(NSString *) pic num:(NSInteger) number;
- (void) setPic:(NSString *) picture;
- (NSString* ) getPic;
- (void) setNum:(NSInteger) tag_number;
- (NSInteger ) getNum;
#end
MapPin.m
#import "MapPin.h"
#implementation MapPin
#synthesize coordinate = _coordinate;
#synthesize title = _title;
#synthesize subtitle = _subtitle;
- (id) initWithCoordinate:(CLLocationCoordinate2D) coord
{
return [self initWithCoordinate:coord title:#""];
}
- (id) initWithCoordinate:(CLLocationCoordinate2D) coord title:(NSString *) title {
return [self initWithCoordinate:coord title:title subtitle:#""];
}
- (id) initWithCoordinate:(CLLocationCoordinate2D) coord title:(NSString *) title subtitle:(NSString *) subtitle {
return [self initWithCoordinate:coord title:title subtitle:subtitle image:#""];}
- (id) initWithCoordinate:(CLLocationCoordinate2D) coord title:(NSString *) title subtitle:(NSString *) subtitle image:(NSString *) pic{
MapPin * me = [[[MapPin alloc] init] autorelease];
me.coordinate = coord;
me.title = title;
me.subtitle = subtitle;
[me setPic:pic];
return me;
}
- (id) initWithCoordinate:(CLLocationCoordinate2D) coord title:(NSString *) title subtitle:(NSString *) subtitle image:(NSString *) pic num:(NSInteger) number{
MapPin * me = [[[MapPin alloc] init] autorelease];
me.coordinate = coord;
me.title = title;
me.subtitle = subtitle;
[me setPic:pic];
[me setNum:number];
return me;
}
- (void) setPic:(NSString*) pic {
picture = pic;
}
- (NSString * ) getPic{
return picture;
}
- (void) setNum:(NSInteger) number {
tag_number = number;
}
- (NSInteger ) getNum{
return tag_number;
}
#end
I have been using cutom map pin created by Mayur Birari, which i tweaked a little bit, to support custom map pin images and id's.
CustomMapPin.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface CustomMapPin : NSObject<MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString* title;
NSString* subtitle;
NSString* pic;
NSInteger tag_number;
}
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString* title;
#property (nonatomic, copy) NSString* subtitle;
#property (nonatomic, copy) NSString* pic;
#property (nonatomic) NSInteger tag_number;
#end
CustomMapPin.m
#import "CustomMapPin.h"
#implementation CustomMapPin
#synthesize title;
#synthesize subtitle;
#synthesize coordinate;
#synthesize pic;
#synthesize tag_number;
- (void)dealloc
{
self.title = nil;
self.pic = nil;
self.subtitle = nil;
[super dealloc];
}
#end
and using it in class like this:
CLLocationCoordinate2D pinlocation;
in a loop I set up required values and create a map pin:
pinlocation.latitude = ...;
pinlocation.longitude = ...;
NSInteger pinID = ....;
CustomMapPin* customMapPin=[[CustomMapPin alloc] init];
customMapPin.coordinate=(CLLocationCoordinate2D
{pinlocation.latitude,pinlocation.longitude};
customMapPin.title=#"title";
customMapPin.subtitle=#"subtitle";
customMapPin.pic = #"customImageName";
customMapPin.tag_number = pinId;
[mapView addAnnotation:customMapPin];
Setting up custom image:
- (MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>) annotation
{
if ([annotation isKindOfClass: [CustomMapPin class]])
{
CustomMapPin * a = annotation;
[annView setImage:[UIImage imageNamed:a.pic]];
}
}
Getting pin id on callout:
- (void)mapView:(MKMapView *)mp annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
CustomMapPin * v = (CustomMapPin *) view.annotation;
int tagNumber = v.tag_number;
....
}
and finally - in my project It was required to have filter buttons - so I needed to remove all pins, and add required. By default calling mapview to remove all annotations created memory leaks. So when I need to clear mapview from annotations, I call this function:
- (void)removeAnnotations
{
NSMutableArray *toRemove = [NSMutableArray arrayWithCapacity:[mapView.annotations count]];
for (id annotation in mapView.annotations)
{
if (annotation != mapView.userLocation)
{
[toRemove addObject:annotation];
}
}
[mapView removeAnnotations:toRemove];
for(int i = 0; i < [toRemove count]; i++)
{
CustomMapPin * a = [toRemove objectAtIndex:i];
[a release];
a = nil;
}
}
Hope this helps
Happy coding! :)
You were just missing dealloc implementation!
for example:
- (void)dealloc
{
[self.title release];
self.title = nil;
self.subtitle release];
self.subtitle = nil;
[super dealloc];
}
Related
I'm trying to extend MKPointAnnotation and add a property to for an NSString called dealLink.
I thought I set it up correctly, but I'm getting this error at the moment:
Property 'dealLink' not found on object of type 'MKPointAnnotation *'
Here's my code in a view controller called MapOffersViewController.m:
#pragma mark - Populate Map
- (void)populateMap:(MKMapView*)map withDeals:(NSArray*) deals
{
NSLog(#"Deals " );
for (NSDictionary *currentDeal in deals) {
CLLocationCoordinate2D ctrpoint;
ctrpoint.latitude = [[[currentDeal objectForKey:#"coords"] objectForKey:#"lat"] doubleValue];
ctrpoint.longitude =[[[currentDeal objectForKey:#"coords"] objectForKey:#"lng"] doubleValue];
MKPointAnnotation *dealAnnotation = [[MKPointAnnotation alloc] init];
dealAnnotation.coordinate = ctrpoint;
dealAnnotation.title = [currentDeal objectForKey:#"vendor"];
**dealAnnotation.dealLink = [currentDeal objectForKey:#"link"];**
NSDictionary *currDict = #{
#"EUR": #"€",
#"GBP": #"₤",
#"USD": #"$",
#"BRL": #"R$"
};
NSString *currName = [currentDeal objectForKey:#"currency"];
NSString *currency = [currDict objectForKey:currName];
dealAnnotation.subtitle = [NSString stringWithFormat:#"%#%i",currency,[[currentDeal objectForKey:#"price"] integerValue ]];
NSLog(#"current deal id is %#",[currentDeal objectForKey:#"id"]);
[map setDelegate:self];
[map addAnnotation:dealAnnotation];
}
}
I'm importing this class called MapAnnotation.h:
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MapAnnotation : MKPointAnnotation
{
NSString *title;
NSString *subtitle;
NSString *dealLink;
CLLocationCoordinate2D coordinate;
}
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#property (nonatomic, retain) NSString *dealLink;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (id)initWithTitle:(NSString *)ttl subTitle:(NSString *)subttl dealLink:(NSString *)dealLnk andCoordinate:(CLLocationCoordinate2D)c2d;
#end
and it's .m:
#import "MapAnnotation.h"
#implementation MapAnnotation
#synthesize title,subtitle,dealLink,coordinate;
- (id)initWithTitle:(NSString *)ttl subTitle:(NSString *)subttl dealLink:(NSString *)dealLnk andCoordinate:(CLLocationCoordinate2D)c2d {
title = ttl;
subtitle = subttl;
dealLink = dealLnk;
coordinate = c2d;
return self;
}
#end
thanks for any help
I used Craig's answer below and this additional code to get this working:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
NSLog(#"callout tapped from del method %#", dealUrl);
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:dealUrl]];
}
- (void)mapView:(MKMapView *)mapView1 didSelectAnnotationView:(MKAnnotationView *)mapView2
{
MapAnnotation *annotation = mapView2.annotation;
NSString *temp = annotation.dealLink;
dealUrl = temp;
// NSLog(#"temp is %#", temp);
}
You're creating an MKPointAnnotation and trying to access dealLink, which is only on MapAnnotations. You need to create a MapAnnotation
....
ctrpoint.longitude =[[[currentDeal objectForKey:#"coords"] objectForKey:#"lng"] doubleValue];
MapAnnotation *dealAnnotation = [[MapAnnotation alloc] init];
dealAnnotation.coordinate = ctrpoint;
....
I keep getting this semantic issue with this code ('MyAnnotation' may not respond to 'initWithDictionary:'), im adding annotations to a map using a plist.
Even though it displays the pin and everything i want it to, i get an semantic issue and cant seem to solve the problem
if anyone could help that would be great thanks
heres the code, the problem is in the //BrewMapViewController.m the error is on this line
MyAnnotation *annotation = [[MyAnnotation alloc] initWithDictionary:breweryDict];
/*MyAnnotation.h*/
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MyAnnotation : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *title;
NSString *subtitle;
NSString *test;
}
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#property (nonatomic, copy) NSString *test;
#end
/*MyAnnotation.m*/
#import "MyAnnotation.h"
#implementation MyAnnotation
#synthesize coordinate, title, subtitle, test;
- (id) initWithDictionary:(NSDictionary *) dict
{
self = [super init];
if (self != nil) {
coordinate.latitude = [[dict objectForKey:#"latitude"] doubleValue];
coordinate.longitude = [[dict objectForKey:#"longitude"] doubleValue];
self.title = [dict objectForKey:#"name"];
self.subtitle = [dict objectForKey:#"address"];
self.test = [dict objectForKey:#"test"];
}
return self;
}
#end
/*BrewMapViewController.h*/
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#interface BrewMapViewController : UIViewController <MKMapViewDelegate> {
IBOutlet MKMapView *map;
NSArray *breweries;
}
#end
/*BrewMapViewController.m*/
#import "BrewMapViewController.h"
#import "MyAnnotation.h"
#implementation BrewMapViewController
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
breweries = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle]
pathForResource:#"test"
ofType:#"xml"]];
double minLat = [[breweries valueForKeyPath:#"#min.latitude"] doubleValue];
double maxLat = [[breweries valueForKeyPath:#"#max.latitude"] doubleValue];
double minLon = [[breweries valueForKeyPath:#"#min.longitude"] doubleValue];
double maxLon = [[breweries valueForKeyPath:#"#max.longitude"] doubleValue];
MKCoordinateRegion region;
region.center.latitude = (maxLat + minLat) / 2.0;
region.center.longitude = (maxLon + minLon) / 2.0;
region.span.latitudeDelta = (maxLat - minLat) * 1.05;
region.span.longitudeDelta = (maxLon - minLon) * 1.05;
map.region = region;
for (NSDictionary *breweryDict in breweries){
MyAnnotation *annotation = [[MyAnnotation alloc] initWithDictionary:breweryDict];
[map addAnnotation:annotation];
[annotation release];
}
}
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation{
if (map.userLocation == annotation){
return nil;
}
NSString *identifier = #"MY_IDENTIFIER";
MKAnnotationView *annotationView = [map dequeueReusableAnnotationViewWithIdentifier:identifier];
if (annotationView == nil){
annotationView = [[[MKAnnotationView alloc] initWithAnnotation:annotation
reuseIdentifier:identifier]
autorelease];
annotationView.image = [UIImage imageNamed:#"beer.png"];
annotationView.canShowCallout = YES;
annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
annotationView.leftCalloutAccessoryView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"pretzel.png"]] autorelease];
}
return annotationView;
}
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"I've been tapped");
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
- (void)dealloc {
[breweries release];
[map release];
[super dealloc];
}
#end
You have to put the method signature for - (id) initWithDictionary:(NSDictionary *) dict into your header file in order to tell BrewMapViewController that the method exists:
/*MyAnnotation.h*/
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MyAnnotation : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *title;
NSString *subtitle;
NSString *test;
}
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle;
#property (nonatomic, copy) NSString *test;
- (id) initWithDictionary:(NSDictionary *) dict;
#end
I want to get the current location's address and set it to the annotation's title. But it didn't work. I think it's because of the block, but I don't know how to fix it. Any help will be appreciated. The most related code is as following:
WhereAmIAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface WhereAmIAnnotation : NSObject <MKAnnotation>
{
CLLocation *myLocation;
NSString *addr;
}
#property (nonatomic, retain) CLLocation *myLocation;
#property (nonatomic, copy) NSString *addr;
#end
WhereAmIAnnotation.m
#import "WhereAmIAnnotation.h"
#implementation WhereAmIAnnotation
#synthesize myLocation, addr;
- (CLLocationCoordinate2D)coordinate;
{
return self.myLocation.coordinate;
}
- (NSString *)title
{
return self.addr;
}
#end
MapViewController.m
- (IBAction)gotoCurrentLocation{
self.mapView.showsUserLocation = YES;//a bar button linked with this action
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
CLLocation *currentLocation = userLocation.location;
MKCoordinateRegion newRegion;
newRegion.center = currentLocation.coordinate;
newRegion.span.latitudeDelta = 0.02;
newRegion.span.longitudeDelta = 0.02;
[self.mapView setRegion:newRegion animated:YES];
WhereAmIAnnotation *whereAmIAnnotation = [[WhereAmIAnnotation alloc] init];
whereAmIAnnotation.myLocation = currentLocation;
whereAmIAnnotation.addr = [self addrAtLocation:currentLocation];
[self.mapView addAnnotation:whereAmIAnnotation];
self.mapView.showsUserLocation = NO;
}
- (NSString *)addrAtLocation:(CLLocation *)location{
[self.geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemark, NSError *error) {
CLPlacemark *topResult = [placemark objectAtIndex:0];
self.addr = [NSString stringWithFormat:#"%# %# %# %#", topResult.country, topResult.locality, topResult.subLocality, topResult.thoroughfare];
}];
return self.addr;
}
I should implement this method
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id )annotation{}
sorry for this stupid question!
You are returning the address while modifying it inside an asynchronous block. You should move the return inside the block, or a create a protocol to handle passing this information back to the caller.
Change the block to this:
- (void)addrAtLocation:(CLLocation *)location{
[self.geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemark, NSError *error) {
CLPlacemark *topResult = [placemark objectAtIndex:0];
whereAmIAnnotation.addr = [NSString stringWithFormat:#"%# %# %# %#", topResult.country, topResult.locality, topResult.subLocality, topResult.thoroughfare];
}];
}
Then make whereAmIAnnotation a member variable. Do you understand WHY this fixes your issues?
Create .h file
#interface MapPinClass : NSObject <MKAnnotation>{
CLLocationCoordinate2D coordinate;
NSString *title;
NSString *subtitle,*color,*index,*locationId;
}
#property (nonatomic, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, copy) NSString *title;
#property (nonatomic, copy) NSString *subtitle,*color,*index,*locationId;
Create .m file
#implementation MapPinClass
#synthesize coordinate,title,subtitle,color,index,locationId;
-(void)dealloc{
[title release];
[super dealloc];
}
write a code where you set
MapPinClass *ann = [[MapPinClass alloc] init];
ann.title = AppMain.Heading;
ann.subtitle = AppMain.Detail;
ann.coordinate = region.center;
[myMapView addAnnotation:ann];
I should implement this method
- (MKAnnotationView *)mapView:(MKMapView *)theMapView viewForAnnotation:(id <MKAnnotation>)annotation{}
Sorry for that stupid question.
I have implemented one dating application in which I have open iPhone map application with source and destination latitude and longitude.But when i touch on the drop pin it display unkown road.But I want to display like user1 and user2 on drop pin.
Please help me for this query
Thanks in advance.
In your View Controller in which you're implementing MapView, you must be implementing MapView's delegate method, viewForAnnotation!!!
Implement it like this:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)annotation {
MKAnnotationView * annotationView = nil;
[mapLoadingIndicatorView stopAnimating];
if ([annotation isKindOfClass:[MKUserLocation class]]) {
[self.mapView.userLocation setTitle:#"User1"];
return annotationView;
}
annotationView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:#"currentAnnotation"];
annotationView.canShowCallout = YES;
return annotationView;
}
And also make sure you've made an AddressAnnotation Class in which
AddressAnnotation.h
#interface AddressAnnotation : NSObject <MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString * title_;
NSString * subtitle_;
}
#property (nonatomic, readwrite, assign) CLLocationCoordinate2D coordinate;
#property (nonatomic, retain) NSString * title_;
#property (nonatomic, retain) NSString * subtitle_;
#end
And AddressAnnotation.m:
#implementation AddressAnnotation
#synthesize coordinate, title_, subtitle_;
- (id)initWithCoordinate:(CLLocationCoordinate2D)c: (NSString *)title: (NSString *)subtitle {
if ((self = [super init])) {
coordinate= c ;
self.title_ = [NSString stringWithFormat:#"%#", title];
self.subtitle_ = [NSString stringWithFormat:#"%#", subtitle];
NSLog(#"title_%#", self.title_);
NSLog(#"subtitle_%#", self.subtitle_);
//self.title_ = #"Title";
//self.subtitle_ = #"Subtitle";
}
return self;
}
- (NSString *)title {
return self.title_;
}
- (NSString *)subtitle {
return self.subtitle_;
}
#end
Wondering how to add another annotation in the mapview a certain distance from current location?
you can use below sample code;
// .h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface AddressAnnotation : NSObject<MKAnnotation> {
CLLocationCoordinate2D coordinate;
NSString *title;
NSString *subtitle;
MKPinAnnotationColor pinColor;
}
#property (nonatomic,retain) NSString *title;
#property (nonatomic,retain) NSString *subtitle;
#property (nonatomic, assign) MKPinAnnotationColor pinColor;
#end
// .m
#import "AddressAnnotation.h"
#implementation AddressAnnotation
#synthesize coordinate;
#synthesize title;
#synthesize subtitle;
#synthesize pinColor;
- (NSString *)subtitle{
return subtitle;
}
- (NSString *)title{
return title;
}
-(id)initWithCoordinate:(CLLocationCoordinate2D) c{
coordinate=c;
return self;
}
#end
currentLocation.latitude = latitudeValue;
currentLocation.longitude = longitudeValue;
AddressAnnotation *addAnnotation = [[AddressAnnotation alloc] initWithCoordinate:currentLocation];
[addAnnotation setTitle:#"Title"];
[addAnnotation setPinColor:MKPinAnnotationColorGreen];
[map addAnnotation:addAnnotation];
[addAnnotation release];
otherLocation.latitude = latitudeValue;
otherLocation.longitude = longitudeValue;
addAnnotation = [[AddressAnnotation alloc] initWithCoordinate:otherLocation];
[addAnnotation setTitle:#"otherTitle"];
[map addAnnotation:addAnnotation];
[addAnnotation release];