MKAnnotation
working - DEVICE ONLY.UPDATE 25-Sep:MapKit 102 has the
MKPlacemark
working.UPDATE 14-Sep:This post was written against MonoTouch beta 0.8; some issues have been addressed in the released version. Updated blog post coming soon...
MapKit seems to have the following 'features' which were supposed to be covered by this sample:
• Display a map
• Change map appearance
• Interact with the user's location via GPS
• Reverse-geocode a location
• Place pins (with popups) on the map
The sample manages to cover four out of five of those, but pin-placement just refused to work...
Firstly, you can download the MapKit.zip (16Kb) if you want to give it a try or just view Main.cs and MainWindow.xib.designer.cs.htm online.
To start with, don't forget you will need
using MonoTouch.MapKit; using MonoTouch.CoreLocation;
in your c#.Some of the 'code highlights' are shown below - I'm not sure if this is the best way to do it, just that it "works" (and if you have any ideas why MKAnnotation is so problematic, let me know).
Show current location
Actually this sample doesn't use MapKit's built-in feature to put a little blue pin on your current position, but it is VERY easy to configure:
mapView.ShowsUserLocation = true;
Change map location
It's also pretty easy to set the map's current center-point (and optionally animate the transition):
mapView.SetCenterCoordinate(new CLLocationCoordinate2D(
Convert.ToDouble(textfieldLatitude.Text),
Convert.ToDouble(textfieldLongitude.Text)),
true);
Respond to map being 'dragged'
When the map is dragged, a
MKMapViewDelegate
subclass is provided to 'listen' for various things and take some action. The MapViewDelegate
class takes a reference to the AppDelegate
so it can interact with the UI.Firstly you must 'attach' the delegate (NOTE: this is NOT a c#
delegate
but a different concept with the same name!)mapView.Delegate = new MapViewDelegate(this);and then do our UI update in the class itself
public class MapViewDelegate : MKMapViewDelegate
{
public override void RegionChanged(MKMapView mapView, bool animated)
{
Console.WriteLine("Region did change");
_appd.labelCurrent.Text = "Map Center " +
mapView.CenterCoordinate.Latitude + ", " +
mapView.CenterCoordinate.Longitude;
}
Reverse geocoding a location
MKReverseGeocoder
follows the same delegate pattern as MKMapView
, first you 'configure' the geocodergeoCoder = new MKReverseGeocoder(mapView.CenterCoordinate);then provide the implementation in another class. In this case MonoTouch.MapKit does not currently provide the method we need, so here is the full class with special MonoTouch attributes included (meaning the c# name
geoCoder.Delegate = new GeoCoderDelegate(this);
geoCoder.Start();
FoundPlacemark
is not important - just make sure you get the parameters correct)public class GeoCoderDelegate : MKReverseGeocoderDelegate
{
AppDelegate _appd;
public GeoCoderDelegate(AppDelegate appd) {_appd = appd;}
// Not currently exposed by MonoTouch, use ExportAttribute
[Export("reverseGeocoder:didFindPlacemark:")]
public void FoundPlacemark(MKReverseGeocoder geocoder
, MKPlacemark placemark)
{
Console.WriteLine("Found in " + placemark.Country);
_appd.labelPlacemark.Text = placemark.SubThoroughfare
+ " " + placemark.Thoroughfare
+ " " + placemark.Locality
+ " " + placemark.AdministrativeArea
+ " " + placemark.Country;
}
GPS Tracking with
CoreLocation
You can spot the
CoreLocation
classes with their CL prefix (as opposed to MK). Once again we have the 'delegate pattern' in play, first creating it with some custom constructor parameters (and not forgetting to start the tracking)locationManager = new CLLocationManager();with the implementation in another class
locationManager.Delegate = new LocationManagerDelegate(mapView, this);
locationManager.StartUpdatingLocation();
private class LocationManagerDelegate : CLLocationManagerDelegateYou can stop it when you are done 'tracking', and you can also set
{
private MKMapView _mapview;
private AppDelegate _appd;
public LocationManagerDelegate(MKMapView mapview, AppDelegate appd)
{
_mapview = mapview; _appd = appd;
}
public override void UpdatedLocation(CLLocationManager manager
, CLLocation newLocation, CLLocation oldLocation)
{
MKCoordinateSpan span = new MKCoordinateSpan(0.2, 0.2);
MKCoordinateRegion region =
new MKCoordinateRegion(newLocation.Coordinate, span);
_appd.mylocation = newLocation;
_mapview.SetRegion(region, true);
_appd.labelInfo.Text = "UserLocation "
+ newLocation.Coordinate.Latitude + ", "
+ newLocation.Coordinate.Longitude;
Console.WriteLine("Location updated");
}
locationManager.DesiredAccuracy
(if you can figure out what the CONST values need to be!).Here's the class diagram (the three delegates are actually nested in AppDelegate, as is a subclass of MKAnnotation which isn't working just yet...)
And yes, the user-interface on this sample isn't exactly intuitive... so here's the "manual" :)
A couple of minor changes required to use the latest release of monotouch. Excellent post on using the map kit.
ReplyDeleteThanks for this great example. Were you able to get the Annotations working with the latest MonoTouch? Looking forward to your updated blog post.
ReplyDeleteI am getting numerous errors trying to build. Does anyone have any suggestions?
ReplyDeleteSee MapKit 102 for MKPlacemark. Still figuring out MKAnnotation issues...
ReplyDeleteHi Craig, any idea where i can find the values of the const for dessired accuracy.
ReplyDeletethankx and really good post!
david
davichu, I'd assumed that the values for the CLLocationAccuracy const were equivalent the meters value in their name, however I hadn't seen anything confirming this until I stumbled across this pyObjC post recently. Dave says:
ReplyDeletekCLLocationAccuracyBest = -1.0
kCLLocationAccuracyNearestTenMeters = 10.0
kCLLocationAccuracyHundredMeters = 100.0
kCLLocationAccuracyKilometer = 1000.0
kCLLocationAccuracyThreeKilometers = 3000.0