UPDATE: Aug 9, 2010
DrawMap2.zip
Note: This does not contain the new MapKit functions for overlaying lines and polygons. This zip was created to compile against 4.0.0 but still have the same codebase.
============================

This is an update to iPhone DevNote #13. This post has solved my zooming/panning problem with a CustomView on top of my MKMapView courtesy of http://spitzkoff.com/craig/?p=108 (Craig’s blog).

The trick here is instead of doing the drawing on the drawRect method of the CustomView, we will use Craig’s methodology to use the drawRect method of a custom MKAnnotationView. Note, that he also used an internal view and made clipsToBounds = NO, this way we can draw the whole geometry on top of MKMapView not just a portion of it. The end result is the shape (polygon in this example) is below the added pins.

polygon_on_top_mapview.png

@interface LinePolygonAnnotationInternalView : UIView
{
	// line view which added this as a subview. 
	LinePolygonAnnotationView* _mainView;
}
@property (nonatomic, retain) LinePolygonAnnotationView* mainView;
@end
 
@implementation LinePolygonAnnotationInternalView
 
@synthesize mainView = _mainView;
 
-(id) init
{
	self = [super init];
	self.backgroundColor = [UIColor clearColor];
	self.clipsToBounds = NO;
 
	return self;
}
 
 
-(void) drawRect:(CGRect) rect
{
	GeometryAnnotation* myAnnotation = (GeometryAnnotation*)self.mainView.annotation;
 
	// only draw our lines if we're not int he moddie of a transition and we 
	// acutally have some points to draw. 
	if(!self.hidden && nil != myAnnotation.points && myAnnotation.points.count > )
	{
		CGContextRef context = UIGraphicsGetCurrentContext(); 
 
		// Drawing lines with a white stroke color
		CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
 
		// Draw them with a 2.0 stroke width so they are a bit more visible.
		CGContextSetLineWidth(context, 2.0);		
 
		if(myAnnotation.geometryType == kGeometryTypePolygon){
			CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
		}
 
		// Draw them with a 2.0 stroke width so they are a bit more visible.
		CGContextSetLineWidth(context, 2.0);
 
		for(int idx = ; idx < myAnnotation.points.count; idx++)
		{
			CLLocation* location = [myAnnotation.points objectAtIndex:idx];
			CGPoint point = [self.mainView.mapView convertCoordinate:location.coordinate toPointToView:self];
 
			NSLog(@"Point: %lf, %lf", point.x, point.y);
 
			if(idx == )
			{
				// move to the first point
				CGContextMoveToPoint(context, point.x, point.y);
			}
			else
			{
				CGContextAddLineToPoint(context, point.x, point.y);
			}
		}
 
		if(myAnnotation.geometryType == kGeometryTypeLine){
			CGContextStrokePath(context);
		}
		else if(myAnnotation.geometryType == kGeometryTypePolygon){
			CGContextClosePath(context);
 
			CGContextDrawPath(context, kCGPathFillStroke);
		}
 
	}
 
 
}
 
-(void) dealloc
{
	self.mainView = nil;
 
	[super dealloc];
}
@end
 
@implementation LinePolygonAnnotationView
 
@synthesize mapView = _mapView;
 
- (id)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
 
		self.backgroundColor = [UIColor clearColor];
 
		// do not clip the bounds. We need the LinePolygonAnnotationInternalView to be able to render the whole line/polygon, regardless of where the
		// actual annotation view is displayed. 
		self.clipsToBounds = NO;
 
		// create the internal line view that does the rendering of the line. 
		_internalView = [[LinePolygonAnnotationInternalView alloc] init];
		_internalView.mainView = self;
 
		[self addSubview:_internalView];
    }
    return self;
}
 
-(void) setMapView:(MKMapView*) mapView
{
	[_mapView release];
	_mapView = [mapView retain];
 
	[self regionChanged];
}
 
-(void) regionChanged
{
	NSLog(@"Region Changed");
 
	// move the internal line view. 
	CGPoint origin = CGPointMake(, );
	origin = [_mapView convertPoint:origin toView:self];
 
	_internalView.frame = CGRectMake(origin.x, origin.y, _mapView.frame.size.width, _mapView.frame.size.height);
	[_internalView setNeedsDisplay];
 
}
 
- (void)dealloc 
{
	[_mapView release];
	[_internalView release];
 
    [super dealloc];
}
 
 
@end

I extended the class above to be able to draw both lines and polygons by checking a property (geometryType) of the GeometryAnnotation. If the geometryType is a line, then just stroke the path. However, if the geometryType is a polygon, then close the path and fill it.

if(myAnnotation.geometryType == kGeometryTypeLine){
	CGContextStrokePath(context);
}
else if(myAnnotation.geometryType == kGeometryTypePolygon){
	CGContextClosePath(context);
 
	CGContextDrawPath(context, kCGPathFillStroke);
}

And here is the GeometryAnnotation class. Most of the code is from Craig, i just added the geometryType property:

//  Created by Craig on 8/18/09.
//  Copyright Craig Spitzkoff 2009. All rights reserved.
//
 
#import "GeometryAnnotation.h"
 
@implementation GeometryAnnotation
@synthesize coordinate = _center;
@synthesize points = _points; 
@synthesize annotationID;
@synthesize geometryType;
 
-(id) initWithPoints:(NSArray*) points withGeometry:(GeometryType)geomType
{
	self = [super init];
 
	geometryType = geomType;
 
	_points = [[NSMutableArray alloc] initWithArray:points];
 
	// create a unique ID for this line so it can be added to dictionaries by this key. 
	self.annotationID = [NSString stringWithFormat:@"%p", self];
 
 
	// determine a logical center point for this line based on the middle of the lat/lon extents.
	double maxLat = -91;
	double minLat =  91;
	double maxLon = -181;
	double minLon =  181;
 
	for(CLLocation* currentLocation in _points)
	{
		CLLocationCoordinate2D coordinate = currentLocation.coordinate;
 
		if(coordinate.latitude > maxLat)
			maxLat = coordinate.latitude;
		if(coordinate.latitude < minLat)
			minLat = coordinate.latitude;
		if(coordinate.longitude > maxLon)
			maxLon = coordinate.longitude;
		if(coordinate.longitude < minLon)
			minLon = coordinate.longitude; 
	}
 
	_span.latitudeDelta = (maxLat + 90) - (minLat + 90);
	_span.longitudeDelta = (maxLon + 180) - (minLon + 180);
 
	// the center point is the average of the max and mins
	_center.latitude = minLat + _span.latitudeDelta / 2;
	_center.longitude = minLon + _span.longitudeDelta / 2;
 
	NSLog(@"Found center of new Annotation at %lf, %ld", _center.latitude, _center.longitude);
 
	return self;
}
 
-(MKCoordinateRegion) region
{
	MKCoordinateRegion region;
	region.center = _center;
	region.span = _span;
 
	return region;
}
 
-(void) dealloc
{
	[_points release];
 
	[super dealloc];
}
 
@end

Now that we have a way to draw a line/polygon as a custom MKAnnotationView, we need a custom TouchView (GeometryTouchView) which could accept the touch events.

For example, if the user wants to draw a line geometry, the GeometryTouchView would accept touch events from the user and add a point as a PointAnnotation in the Map. Succeeding points would be added to an array. For every point added, the MKAnnotationView drawRects method connects the points to produce a line. The MKAnnotationView is now added to the map.

Once the geometry is added as an annotation, the custom TouchView is hidden. This way we have access (panning/zooming) to the mapview. If we make a pan or a zoom, the region changes, thus we need to redraw the shape of the annotation again.

- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
	if(currentAnnotationView != nil){
		NSLog(@"regionWillChangeAnimated");
 
		currentAnnotationView.hidden = YES;
	}
}
 
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
	if(currentAnnotationView != nil){
		NSLog(@"regionDidChangeAnimated");
 
		currentAnnotationView.hidden = NO;
		[currentAnnotationView regionChanged];
	}
}
-(void) regionChanged
{
	NSLog(@"Region Changed");
 
	// move the internal line view. 
	CGPoint origin = CGPointMake(, );
	origin = [_mapView convertPoint:origin toView:self];
 
	_internalView.frame = CGRectMake(origin.x, origin.y, _mapView.frame.size.width, _mapView.frame.size.height);
	[_internalView setNeedsDisplay];
 
}

The resulting image is now:
polygon_small_on_top_of_mapview.png

(Download the DrawMap.zip code.) – old. This is for iOS < 4