Thursday, July 11, 2013

How to create custom map icons in uimapview in iOS 6 with ARC

For this example, we will assume you are making a real estate app since most people that want this are. It looks a little confusing at first but once you get over the scary factor, it is very easy.

You will need;

-an MKAnnotation object (this holds all the details about each object so it is easier to find)
-an MKAnnotationview object (this holds all the unique view details about the object)
-an MKMapView to put it on (this is the map itself)

we will call our annotation PropertyUnit and our annotation view called PropertyUnitView.

We have created an object called "PropertyListing" with the actual details of our property. We are linking one to one for each unit.
in PropertyUnit.h;
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#import "PropertyListing.h"

@interface PropertyUnit : NSObject <MKAnnotation>{
    
}

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *address;
@property (nonatomic, assign) CLLocationCoordinate2D theCoordinate;
@property (nonatomic, strong) PropertyListing *property;
//propertyListing is just or actual details for the object and can be anything 


- (id)initWithName:(NSString*)name address:(NSString*)address coordinate:(CLLocationCoordinate2D)coordinate;
- (MKMapItem*)mapItem;


@end

we need to assign information when someone clicks on the item in the map, they are commonly set as "title" and "subtitle" we are just assigning our own information to these texts.
in PropertyUnit.m;
#import "PropertyUnit.h"
#import <AddressBook/AddressBook.h>


@implementation PropertyUnit

- (id)initWithName:(NSString*)name address:(NSString*)address coordinate:(CLLocationCoordinate2D)coordinate {
    if ((self = [super init])) {
        if ([name isKindOfClass:[NSString class]]) {
            self.name = name;
        } else {
            self.name = @"Unknown charge";
        }
        self.address = address;
        self.theCoordinate = coordinate;
        
    }
    return self;
}

- (NSString *)title {
    return _address;
}

- (NSString *)subtitle {
    return [NSNumberFormatter localizedStringFromNumber:_property.price numberStyle:NSNumberFormatterCurrencyStyle];
}

- (CLLocationCoordinate2D)coordinate {
    return _theCoordinate;
}

- (MKMapItem*)mapItem {
    NSDictionary *addressDict = @{(NSString*)kABPersonAddressStreetKey : _address};
    
    MKPlacemark *placemark = [[MKPlacemark alloc]
                              initWithCoordinate:self.coordinate
                              addressDictionary:addressDict];
    
    MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
    mapItem.name = self.title;
    
    return mapItem;
}

@end

The views are very basic and supposedly don't even really need to exist because most of the custom details are done in the main mapview but I left it here for consistancey sake. 
In PropertyUnitView.h;

#import <MapKit/MapKit.h>
#import "PropertyListing.h"

@interface PropertyUnitView : MKAnnotationView

@property (nonatomic, strong) PropertyListing *property;

@end

In PropertyUnivewView.m;
#import "PropertyUnitView.h"

@implementation PropertyUnitView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

@end

Now for the real meat and potatoes.
In MapViewController.h
#import <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import "PropertyListing+methods.h"
#import "PropertyUnit.h"
#import "PropertyUnitView.h"
#import "PropertyInfoViewController.h"
#import "ScrollNavigationViewController.h"
#import "SearchViewController.h"
#define METERS_PER_MILE 1609.344

@interface MapViewController : UIViewController <MKMapViewDelegate> {
}

@property(nonatomic,retain) MKMapView *mapView;

@end

in MapViewController.m

@implementation MapViewController
-(id)init{
    self = [super init];
    if (self){
        //create the mapview
        _mapView = [[MKMapView alloc] init];
        [self.view addSubview:_mapView];
        if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone){
            //iphone
            _mapView.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y-20, self.view.frame.size.width, self.view.frame.size.height+27);
        }else{
            //ipad
            _mapView.frame = CGRectMake(self.view.frame.origin.x-20, self.view.frame.origin.y, 1024, self.view.frame.size.height-90);
        }
        _mapView.delegate = self;
    }
        return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    _mapView.showsUserLocation = YES;
    [self setDefaultTorontoView];
}

-(void)setDefaultTorontoView{
    //center on the office and create a zoom level to go by. 
    CLLocationCoordinate2D zoomLocation;
    zoomLocation.latitude = 43.662431;
    zoomLocation.longitude= -79.383667;
    
    MKCoordinateRegion viewRegion = MKCoordinateRegionMakeWithDistance(zoomLocation, 120000, 120000);
    
    [_mapView setRegion:viewRegion animated:YES];
}

#pragma mark - unit detail methods

- (NSInteger)numberOfUnits
{
    id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][0];
    return [sectionInfo numberOfObjects];
}

-(void)printframe:(CGRect)frame{
    //just a way to print off the frames if needed
    NSLog(@"current x:%.0f, y:%.0f, length:%.0f, height:%.0f",frame.origin.x, frame.origin.y,frame.size.width,frame.size.height);
}

#pragma mark - map delegate

- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views{
    //called Tells the delegate that one or more annotation views were added to the map.
    if (existingProperties == nil){
        existingProperties = [[NSMutableArray alloc] init];
    }
    for (PropertyUnitView *annView in views)
    {
        if (![existingProperties containsObject:annView]){
            CGRect endFrame = annView.frame;  
            annView.frame = CGRectMake(endFrame.origin.x, endFrame.origin.y + endFrame.size.height, endFrame.size.width, 0);
            //NSLog(@"Start point");
            //[self printframe:annView.frame];
            //[self printframe:endFrame];
            [UIView animateWithDuration:0.5 animations:^{ annView.frame = endFrame; }];
            [existingProperties addObject:annView];
        }
    }
}

- (PropertyUnitView *)mapView:(MKMapView *)map viewForAnnotation:(id <MKAnnotation>)annotation {
    static NSString *annotationViewReuseIdentifier = @"annotationViewReuseIdentifier";
    PropertyUnitView *annotationView = (PropertyUnitView *)[_mapView dequeueReusableAnnotationViewWithIdentifier:annotationViewReuseIdentifier];
    if (annotationView == nil)
    {
        annotationView = [[PropertyUnitView alloc] initWithAnnotation:annotation reuseIdentifier:annotationViewReuseIdentifier];
    }
    if ([annotation isMemberOfClass:[PropertyUnit class]]){
        //if the annotation is a property 
        annotationView.image = [UIImage imageNamed:@"house20.png"];
        annotationView.draggable = FALSE;
        annotationView.canShowCallout = YES;
        UIButton *goToDetail = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
        annotationView.rightCalloutAccessoryView = goToDetail;
    } else {
        //it is probably the MKUserLocation
        if ([annotation isKindOfClass:[MKUserLocation class]]) {
        }
    }
    annotationView.annotation = annotation;
    return annotationView;
}

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
    PropertyUnit *unit = (PropertyUnit*)view.annotation;
    PropertyListing *listing = unit.property;
    PropertyInfoViewController *infoView = [[PropertyInfoViewController alloc] initWithPropertyListing:listing]; 
    [self.navigationController pushViewController:infoView animated:YES];
}
@end

No comments:

Post a Comment