Sunday 27 September 2009

MonoTouch MapKit 103

UPDATE 13-Oct: This code all works in the latest version of MonoTouch - v1.1 - on devices AND in the simulator. You can download a MonoDevelop solution or view the c# files:UPDATE 28-Sep:confirmation from Geoff that the problem with the Simulator should be fixed in 1.1, coming soon...

Once again thanks to a hint from Geoff, I've pieced a little more of MapKit together. This has been tested with RC2 of MonoTouch. The trick? It ONLY WORKS ON THE DEVICE - the simulator will 'crash' with the Stack Trace shown at the bottom of this post (I'm sure this will be fixed in a future release).



This code in FinishedLaunching
Once again, use the 'new' AddAnnotationObject method.
mapView.Delegate = new MapViewDelegate(this);
MyAnnotation a = new MyAnnotation(
new CLLocationCoordinate2D(-33.867139,151.207114)
, "Home"
, "is where the heart is"
);
Console.WriteLine(" ### This breaks in the simulator BUT it works on device!!!");
mapView.AddAnnotationObject(a);
with this MKAnnotation subclass
In MonoTouch MKAnnotation is an abstract class, but in ObjectiveC it's a @protocol. This kinda explains why everything in the class is read-only - @protocols are akin to interfaces in C#, but they cannot be "modelled" as interfaces in MonoTouch because @protocols members can be optional! The simplest way to get an annotation is therefore this implementation (although I think you could also create your own subclass of NSObject and Export() the MKAnnotation members manually).
public class MyAnnotation : MKAnnotation
{
private CLLocationCoordinate2D _coordinate;
private string _title, _subtitle;
public override CLLocationCoordinate2D Coordinate {
get { return _coordinate; }
}
public override string Title {
get { return _title; }
}
public override string Subtitle {
get { return _subtitle; }
}
/// <summary>
/// Need this constructor to set the fields, since the public
/// interface of this class is all READ-ONLY
/// <summary>
public MyAnnotation (CLLocationCoordinate2D coord,
string t, string s) : base()
{
_coordinate=coord;
_title=t;
_subtitle=s;
}
}
and this MKMapViewDelegate implementation
public class MapViewDelegate : MKMapViewDelegate
{
private AppDelegate _appd;
public MapViewDelegate (AppDelegate appd):base()
{
_appd = appd;
}
///
/// When user moves the map, update lat,long text in label
///

public override void RegionChanged
(MKMapView mapView, bool animated)
{
Console.WriteLine("Region did change");
_appd.labelCurrent.Text = "Map Center "
+ mapView.CenterCoordinate.Latitude + ", "
+ mapView.CenterCoordinate.Longitude;
}
///
/// Seems to work in the Simulator now
///

public override MKAnnotationView GetViewForAnnotation
(MKMapView mapView, NSObject annotation)
{
var anv = mapView.DequeueReusableAnnotation("thislocation");
if (anv == null)
{
Console.WriteLine("creating new MKAnnotationView");
anv = new MKPinAnnotationView(annotation, "thislocation");
}
else
{
anv.Annotation = annotation;
}
anv.AnimatesDrop = true;
anv.PinColor = MKPinAnnotationColor.Green;
anv.CanShowCallout = true;
return anv;
}
}

This Stack Trace (below) is what you'll see in MonoDevelop when using the Simulator - upload to the device to get it working (or if you are using the trial version, you may have to wait for an update to MonoTouch). UPDATE:fixed in MonoTouch 1.1
  at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr) <0x00004>
at (wrapper managed-to-native) MonoTouch.UIKit.UIApplication.UIApplicationMain (int,string[],intptr,intptr)
at MonoTouch.UIKit.UIApplication.Main (string[],string,string)
at MonoTouch.UIKit.UIApplication.Main (string[])
at MapKit01.Application.Main (string[])
at (wrapper runtime-invoke) .runtime_invoke_void_object (object,intptr,intptr,intptr)
Native stacktrace:
0 MapKit01 0x00092d1a mono_handle_native_sigsegv + 266
1 MapKit01 0x00006f3a mono_sigsegv_signal_handler + 298
2 libSystem.B.dylib 0x90b25b9b _sigtramp + 43
3 ??? 0xffffffff 0x0 + 4294967295
Debug info from gdb:
warning: Trying to remove a section from the ordered section list that did not exist at 0x2d4000.

Friday 25 September 2009

MonoTouch MapKit 102

UPDATE 27-Sep:MapKit 103 has the MKAnnotation working - DEVICE ONLY.

Thanks to Geoff and a bit of ObjC sample code I've figured out a little bit more of MapKit with MonoTouch. MKPlacemarks now appear (although I'm still working on MKAnnotation).

The main changes from MapKit 101 are:

FoundWithPlacemark
Now calls the *Object method added by Geoff.
_appd.mapView.AddAnnotationObject(placemark);

MapViewDelegate
Updated to pass the annotation into the constructor of MKPinAnnotationView and also set the properties for animation, color and showing the callout when clicked.
public override MKAnnotationView GetViewForAnnotation
(MKMapView mapView, NSObject annotation)
{
Console.WriteLine("get view MKAnnotation "+annotation);
var anv = mapView.DequeueReusableAnnotation("thislocation");
if (anv == null)
{
Console.WriteLine("creating new MKAnnotationView");
anv = new MKPinAnnotationView(annotation, "thislocation");
}
else
{
anv.Annotation = annotation;
}
anv.AnimatesDrop = true;
anv.PinColor = MKPinAnnotationColor.Green;
anv.CanShowCallout = true;
return anv;
}
Hopefully I can figure out the MKAnnotation issue too...

Thursday 24 September 2009

#ScottGuAnnouncement : WebsiteSpark (free IIS7)

There was certainly a lot of 'buzz' about #ScottGuAnnouncement on Twitter... although I'm not sure it has lived up to the hype...

Seems like the main announcement was 'BizSpark-lite' - WebsiteSpark - which lowers the cost barrier to small companies 'starting out' with Microsoft. Meh.

More interesting (to me) was Web Platform Installer v2... I don't know whether it already did this - but there is now the ability to install IIS7 very easily. If this has been around for ages without me realizing, all I can say is "d'oh".




So should we now just use IIS7 with Visual Studio 2008/10? Is Cassini dead?


p.s. not sure what to make of this announcement: "Microsoft Silverlight to run on Moblin devices"? And Microsoft has let Novell toil away on Moonlight why?

Sunday 20 September 2009

Silverlight mapanimation

Taking a small break from MonoTouch to update RaceReplay.net.

Click to view each image


or if you already have Silverlight, watch the animations of today's Sydney Running Festival
Missing MonoTouch?
To get your MonoTouch fix for the day, I recommend the following screencasts by Brent Schooley:Highly recommended.

Friday 18 September 2009

MonoTouch Info.plist & Default.png

I'm the first to admit this is a somewhat lazy post... but hey it's Friday. There's not a lot of MonoTouch-C#-specific content today - but .NET developers coming to the iPhone might not be aware of this stuff, so here goes:

Default.png
AKA "Splash screen", if you place a file named Default.png in the root of your application with Build action: Content then the iPhone will automatically display this while the application loads (rather than the "black screen").
There's a few approaches you can take: the awesome Flight Control app uses a simple 'Please wait' banner (which conveniently prompts you to turn the phone sideways); my other sample uses a 'greyed out' copy of its UI with a 'loading...' message.

Alternatively you could take a screenshot before shut-down and save it as /Name.app/Default.png, so the next time it starts up it looks like you are continuing where you left off - even before the app is finished loading. UPDATE: here's some sample screenshot code to add to your WillTerminate method

Info.plist
Whether you realise it or not, your MonoTouch app already contains an Info.plist file - right-click on your project → OptionsiPhone ApplicationApplication Bundle - these fields all end up in a MonoTouch-generated Info.plist.


If you navigate with Finder to /Projects/<your project>/bin/iPhone/debug/ and remove the .app extension you can open your 'app' (it's just a folder!) then you can view the generated Info.plist that is sent to your device.


That doesn't mean you are restricted from adding more details though! Remember that plist files are basically just a special 'Apple' form of XML. Simply add a text file to the root of your project and rename it Info.plist - MonoTouch will merge it with the Options before copying to the simulator/device. Here is a sample Info.plist (described below). DON'T FORGET to set the Build action:Content in the file's properties.

SBUsesNetwork & UIRequiresPersistentWiFi
Indicates to the operating system that your app uses connectivity. The Persistent setting keeps the WiFi running (so beware of running down the battery).


UIPrerenderedIcon
When true it tells the operating system that your icon is already pretty, and not to mess with it! When false the icon is made shiny with curved corners for you.

You probably want to leave it as the default: false!

UIStatusBarHidden
If true, totally HIDES the status bar (eg. for games such as Flight Control).

UIStatusBarStyle
Use this to turn the status bar from grey to black! The UIPrerenderedIcon example above also shows the UIStatusBarStyleOpaqueBlack status bar style.

UIInterfaceOrientation
If you change this from the default setting (eg. to UIInterfaceOrientationLandscapeRight) you should ensure you have provided landscape views in your application. You really don't want this effect:


CFBundleURLTypes
If you read about initiating a call or other actions in MonoTouch you'll know about the use of OpenURL schemes to open other applications.

Using CFBundleURLTypes in Info.plist informs the operating system of what schemes your application will respond to. If you use the sample Info.plist and view this blogpost on your iPhone, clicking initdial://123456789 will open your app! You probably also want to parse the parameters... but that's another post :)

Custom schemes are really useful for social apps (eg. twitter clients, map stuff, etc). To learn more about what schemes are already available/used check out handleopenurl.com.


There are many more options you can set in Info.plist, and also other ways to control some of the things above (such as showing/hiding and changing the appearance of the status bar). Check out some of the Objective-C resources and try them for yourself!

Monday 14 September 2009

MonoTouch released

MonoTouch 1.0 has been released and is available for purchase!
MonoTouch logo

Phonebook sample updated

Thank's to Simon's Build a Customer UITableViewCell post the Corporate Phonebook sample has been updated: now featuring custom UITableViewCells (and the phonenumbers don't overflow any more). Read more in the previous post.



The project can be downloaded (29Kb) to try out on your newly registered copy of MonoTouch! Maybe having the company phonebook on the iPhone will help convince your employer to spring for an Enterprise licence :)

Friday 11 September 2009

MonoTouch with SQLite "Corporate Phonebook"

UPDATE [3-May-12]: The latest code for this sample is now on github (CorporateDirectory). It contains the latest version of SQLite.cs and minor bugfixes. Firstly, let me begin by saying this sample totally relies on the contribution of Frank Krueger who posted the SQLClient.cs code to the MonoTouch mailing list
Here is the code. Consider it released into the public domain. If there's interest, I can start a Google code project or something.
Many thanks Frank! This is a VERY simple application using that library: it lists 'employees' from an SQLite database and allows you to call or email them:
*
* note: may not adhere to iPhone user interface guidelines!

The c# code itself can be downloaded (26Kb) or browsed below:
This first sample only reads from SQLite on the iPhone - just a single database call
using (var db = new SQLiteClient.SQLiteConnection("phonebook")) {
db.Open();
var users = db.Query(
"SELECT Firstname, Lastname, Work, Mobile,
Department, Email
FROM Phonebook ORDER BY Lastname"
, 1000);
listData = users.ToList();
}
which queries data that was set-up in the SQLite Database Browser and included with the application as a 'Content' file within the MonoTouch project.
Creating a database using SQLite Database Browser


It uses UITableViewDelegate, UITableViewDataSource and UIAlertViewDelegate implementations to populate the scrolling list and react to touch 'events'.

When you touch a row, we use the OpenUrl method discussed previously to trigger a call or email.

Future additions to this sample might include alphabetized sections, search function and a proper 'user page' rather than using UIAlertView. Perhaps some hierarchical navigation and an online 'updater' function as well?

p.s. yes, I shouldn't have used INTEGER for the telephone numbers in SQLite... they seem to be overflowing. I will convert them to TEXT in a future post..

Sunday 6 September 2009

MonoTouch MapKit 101

UPDATE 27-Sep:MapKit 103 has the 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...

MonoTouch MapKitMapKit 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 geocoder
geoCoder = new MKReverseGeocoder(mapView.CenterCoordinate);
geoCoder.Delegate = new GeoCoderDelegate(this);
geoCoder.Start();
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 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();
locationManager.Delegate = new LocationManagerDelegate(mapView, this);
locationManager.StartUpdatingLocation();
with the implementation in another class
private class LocationManagerDelegate : CLLocationManagerDelegate
{
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");
}
You can stop it when you are done 'tracking', and you can also set 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" :)

Thursday 3 September 2009

Initiate a call with MonoTouch

Initiating calls, text messages and triggering other applications on the iPhone is a pretty basic requirement for MonoTouch applications - but it still took me a little while to "translate" the examples I found (eg developertips).






The mechanism is pretty simple - you just need to construct a Url with a specific 'scheme' and call the OpenURL method. I kept finding examples using self to reference OpenURL so it took me a second to figure out the method is actually on UIApplication. I'm working with iPhone OS 3.0 so OpenURL returns a bool if the action is 'possible': false if that scheme is not recognised on the device.
public partial class AppDelegate : UIApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
window.MakeKeyAndVisible();
buttonCall.TouchDown += delegate { // trigger action
NSUrl url = new NSUrl("tel:" + textfieldInput.Text);
if (!UIApplication.SharedApplication.OpenUrl(url))
{
var av = new UIAlertView("Not supported"
, "Scheme 'tel:' is not supported on this device"
, null
, "Ok thanks"
, null);
av.Show();
}
};
* Note: you can read about the singleton SharedApplication property in the UIApplication reference.

Some of the other common schemes look like this:
new NSUrl("sms:" + textfieldInput.Text);
new NSUrl("http://maps.google.com/maps?q=" + textfieldInput.Text); // opens Maps
new NSUrl("http://" + textfieldInput.Text); // opens Safari
new NSUrl("mailto:you@gmail.com?subject=" + textfieldInput.Text);
and two others I've added for testing are
new NSUrl("tweetie:///post?message=" + textfieldInput.Text);
new NSUrl("comgoogleearth://");
You can view the full code for the test app - Main.cs and MainWindow.xib.designer.cs - the Xib is basically a textfield and seven buttons - complete source in MonoTouch solution ZIP (14k).

Here's a link to Apple's Url Scheme Reference (PDF)
and two lists of the currently available url types: iPhone Url Schemes & AppLookup