Tuesday, 30 September 2014

iPhone 6 and 6 Plus LaunchScreen.storyboard for Xamarin

Following on from the previous post about adding launch images for iPhone 6 and 6 Plus, here are the instructions for adding a LaunchScreen.storyboard file instead of multiple fixed-size images. Apple's documentation recommends this method over using the static images. I used these instructions for replacing launch images with storyboards as a reference.

Configuring a LaunchScreen like this will automatically scale up your app for iPhone 6 and iPhone 6 Plus devices. You might also want to consider adding @3x retina images for iPhone 6 Plus support.

1. Add a new Storyboard to your project and call it LaunchScreen.storyboard.

2. Drag a UIViewController in and design your launch screen. I chose a black background with some white centered text - it looks like this (use the VIEW AS option to preview in different sizes):
3. Open the iPhone application Project Options and scroll down to the iPhone Launch Images section. There is a new Launch Screen dropdown (currently in Beta) that will automatically be populated with the available storyboards and xibs in your project. Choose the storyboard you just added.
3a. This creates the following key in your Info.plist (just FYI):
 <key>UILaunchStoryboardName</key>
 <string>LaunchScreen</string>

4. When you build the app, appropriate launch images will be generated for your app. Here's a shot of the emulator starting up showing the launch image for iPhone 6:

I've updated my Xamarin.Forms Todo sample, the code and storyboard are available on github.

UPDATE: Gerry reminded me about Marco's experience where adding a LaunchImage.storyboard file causes iOS to ignore the UIDeviceFamily setting in Info.plist (which specifies iPhone/iPod, iPad, or Universal app) and upscale iPhone-only apps to full iPad screen size (potentially making your app look really weird!). This behavior still appears to occur on the simulator, so test before you launch :)




Monday, 22 September 2014

iPhone 6 and 6 Plus Launch Images for Xamarin

I was initially stumped by how to get my Xamarin.iOS and Xamarin.Forms apps to size correctly on the iPhone 6 and iPhone 6 Plus. Thanks to this StackOverflow question & answer I have a solution - reposting here because the solution that works best for me right now is only the 3rd most popular answer there.

Simply create two new default images (this is for portrait only, but landscape will become obvious later):

Default-667h@2x.png for iPhone 6; dimensions 750x1334

Default-736h@3x.png for iPhone 6 Plus; dimensions 1242x2208

and place them in the application root or the Resources folder. Notice the filename format is similar to the Default-568h@2x.png image that Apple introduced for the iPhone 5 screen.

Now edit the source of your Info.plist file (open in a text editor so you can type XML directly) and add the following UILaunchImages key (the first two items are for iPhone 6, the others are for the older default image configuration):

<key>UILaunchImages</key>
<array>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>8.0</string>
<key>UILaunchImageName</key>
<string>Default-667h</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{375, 667}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>8.0</string>
<key>UILaunchImageName</key>
<string>Default-736h</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{414, 736}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageName</key>
<string>Default-568h</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{320, 568}</string>
</dict>
 <dict>
  <key>UILaunchImageMinimumOSVersion</key>
  <string>6.0</string>
  <key>UILaunchImageName</key>
  <string>Default</string>
  <key>UILaunchImageOrientation</key>
  <string>Portrait</string>
  <key>UILaunchImageSize</key>
  <string>{320, 480}</string>

 </dict>
</array>
If you wish to support landscape images, add matching keys with Default-Landscape-???h filenames and specify the correct orientation and size.

Note that this is not Apple's preferred way of indicating support for the screen sizes. Their Launch Images doc says:

You use a launch XIB or storyboard file to indicate that your app runs on iPhone 6 Plus or iPhone 6.

which requires you to create a Storyboard or XIB using size classes. More on how to do that in this post, or head back to that StackOverflow post!

p.s. this iPhone 6 Screens Demystified post by PaintCode is awesome!

UPDATED: @jamesmontemagno informs me that you need to add the original 320x480 into the plist too, so I've added to the example above. 

Sunday, 29 September 2013

Android TextToSpeech API with Xamarin: it talks too!

A recent post covered Apple's new text-to-speech API in iOS 7, but forgot to mention that Android has actually had this capability for a while! It's really easy to add text-to-speech to a Xamarin.Android app: just implement TextToSpeech.IOnInitListener (which is a single method: OnInit) then create a new TextToSpeech instance:

speaker = new TextToSpeech (this, this);

and call Speak:

void Speak(string text) {
   var p = new Dictionary ();
   speaker.Speak (text, QueueMode.Flush, p);
}

The TaskyPro sample code has been updated so that both the iOS and Android apps have a Speak button. The Android app looks like this:

Check out this cool TtsSetup sample for Xamarin (via StackOverflow) for more details on how to customize the Android TextToSpeech API.

Friday, 27 September 2013

Built-in Barcode Scanning with iOS7 and Xamarin: MonkeyScan!

Another new iOS 7 feature is built-in support for barcode-scanning via the AVFoundation AVCaptureDevice API. Back in 2012 I threw together MonkeyScan using Windows Azure Services and the ZXing barcode scanning library. For iOS 7 I've updated the code to use the Azure Mobile Services Component and the new iOS 7 barcode scanning API instead.

The app looks like this when scanning a PassKit pass:

The code that sets up an AVCaptureDevice for 'metadata capture' (as opposed to capturing an image or video, I guess :) is shown below:

bool SetupCaptureSession () {
   session = new AVCaptureSession();
   AVCaptureDevice device = 
      AVCaptureDevice.DefaultDeviceWithMediaType(AVMediaType.Video);
   NSError error = null;
   AVCaptureDeviceInput input = 
      AVCaptureDeviceInput.FromDevice(device, out error);

   if (input == null)
      Console.WriteLine("Error: " + error); 
   else
      session.AddInput(input);

   AVCaptureMetadataOutput output = new AVCaptureMetadataOutput();
   var dg = new CaptureDelegate(this);
   output.SetDelegate(dg, MonoTouch.CoreFoundation.DispatchQueue.MainQueue);
   session.AddOutput(output); // MUST add output before setting metadata types!

   output.MetadataObjectTypes = new NSString[] 
      {AVMetadataObject.TypeQRCode, AVMetadataObject.TypeAztecCode};

   AVCaptureVideoPreviewLayer previewLayer = new AVCaptureVideoPreviewLayer(session);
   previewLayer.Frame = new RectangleF(0, 0, 320, 290);
   previewLayer.VideoGravity = AVLayerVideoGravity.ResizeAspectFill.ToString();
   View.Layer.AddSublayer (previewLayer);

   session.StartRunning();
   return true;
}

You can specify specific barcodes to recognize or use output.AvailableMetadataObjectTypes to process all supported types.

...and it speaks!
Since the app now requires iOS 7, it can also use the new AVSpeechSynthesizer to speak the scan result as well (see previous post).

if (valid && !reentry) {
   View.BackgroundColor = UIColor.Green;
   Speak ("Please enter");
} else if (valid && reentry) {
   View.BackgroundColor = UIColor.Orange;
   Speak ("Welcome back");
} else {
   View.BackgroundColor = UIColor.Red;
   Speak ("Denied!");
}

The MonkeyScan github repo has been updated with this code.

Thursday, 26 September 2013

iOS SpeechSynthesizer API with Xamarin: it talks!

Mike posted a neat code example today on adding the new iOS 7 AVSpeechSynthensizer API to a Xamarin app.

It's so easy, I added speech synthesis to the this TaskBoard to-do list example in about 5 lines of code. Now the app can read the to-do item back to you :) just by adding this code:

if (UIDevice.CurrentDevice.CheckSystemVersion (7, 0)) {
   SpeakButton.TouchUpInside += (sender, e) => {    // requires iOS 7
      Speak (TitleText.Text + ". " + NotesText.Text);
   };
}

and

void Speak (string text) {
   var speechSynthesizer = new AVSpeechSynthesizer ();

   var speechUtterance = new AVSpeechUtterance (text) {
      Rate = AVSpeechUtterance.MaximumSpeechRate/4,
      Voice = AVSpeechSynthesisVoice.FromLanguage ("en-AU"),
      Volume = 0.5f,
      PitchMultiplier = 1.0f
   };

   speechSynthesizer.SpeakUtterance (speechUtterance);
}

The UI now looks like this: touch the Speak button to hear the text read back to you.

Wednesday, 10 July 2013

New Dropbox API one day, on Xamarin the next

Long-time Xamarin customers know that there is a history of delivering updates same day when the native platforms (iOS and Android, for example) are upgraded. Today there was another little surprise - the new Dropbox Datastore API that was announced at DBX yesterday was made available via a Xamarin Component today!

Think of it as a cross-platform iCloud-like key-value store ~ same kind of stuff you can do with Parse or even Azure Mobile Services.

Check out the Xamarin Blog for all the details and a cute sample app called MonkeyBox.

But wait, there's more!
In the past I've posted about porting a small "To Do List" application to various different platforms, including Azure (iOS, Android, Mac and more) twice as well as an iCloud version for iOS.

Here's the same basic code using the new Dropbox Datastore API (just iOS, for now...):

Here's a direct link to the code on github... to use just download the solution (which includes the Xamarin Dropbox Component) and:

  1. Visit the Dropbox App Console to get your access credentials set-up.
  2. Add the App key and App secret to the AppDelegate.cs in the project.
  3. Add a Custom URL Type to the advanced Info.plist settings (replacing the identifier and also YOUR_APP_KEY with the correct credential value) like this

When the app is up-and-running, you can 'browse datastores' from the Dropbox App Console and see the data update on the server in real-time!

In only a few lines of code you can use a cloud-based data store simply and easily, across multiple platforms and devices! Load up the app on a couple of iOS devices (and/or the iOS Simulator) and watch the magic in action :)

Tuesday, 15 January 2013

Xamarin for Java Developers

Last night I was given the opportunity to present Xamarin's Mono for Android to the East Bay Google Developer Group. It was a lot of fun, with plenty of the audience asking a lot of great questions and generally being curious about how C# can be used to build Android apps. Here are the slides from the talk - please drop me a line with any feedback or questions!

One topical question was "what does using Xamarin and C# offer existing Android Java developers?". I have a couple of thoughts which are listed below (in no particular order):

Learn a new language!
The days of specializing in a single language throughout your career (or even throughout the year!) are over. Once upon a time it was COBOL all the way down, and you could argue that it's still enough to just know C/C++. But it's no longer the norm ~ developers want to grow and learn, and use the best tool for the job (whether it's Perl, PHP, Java, Ruby, C#, F#, Javascript, TypeScript, Go, or whatever).

It can be daunting though, to start from scratch and learn a new IDE, framework and syntax - which is why I think C# is a great progression for Java devs. The two languages are obviously related - there is a lot of common syntax and equivalent keywords that make it easy to get proficient quickly. They're both strongly typed, so the IDE and compiler can really help you learn. Then once you know the C# basics, a whole world of new APIs open up to you: LINQ, the .NET framework's serialization and web service features, Parallel Task Library and async support, reactive extensions, and more.

It also introduces you to another IDE (Visual Studio is a great tool, as is Xamarin's free MonoDevelop IDE), which in turn can encourage you to further explore other languages and platforms (suddenly it's a small leap to work on Windows Phone, or try F#). Using the free trial you can easily get productive in C# and Android development without any up-front cost: you can run your apps on the Android emulator, and choose to work on either Windows or Mac with MonoDevelop.

Cross-platform development!
There are a lot of Java developers who are already proficient with the Android SDK and have published successful apps to Google Play, or Amazon, or Samsung app stores; and there's another group of corporate/enterprise developers building apps for internal/extranet use that were designed for Android. When these developers ask "why Xamarin?", I never say "throw away everything you've done and re-write in C#!" -- that'd be crazy ;-)

But what happens when you want to take these apps cross-platform, either to make more money on the iOS and Mac App Stores or to better support a corporate BYOD policy which means your internal apps need to run on iOS and Windows? You can choose to learn Objective-C to write for iOS and C# for Windows... or take the opportunity provided by Xamarin to use C# for all the non-Java platforms and reduce the amount of code you have to support and re-write by half!

Following on from the first point, I think learning C# is a much smaller leap for a Java developer than Objective-C; and once you've done so you can work on either iOS apps using Xamarin's MonoTouch or Windows Phone, Windows 8 and Windows RT tablet apps! You might like C# so much that you do go back and re-write your Java, or at least start building a hybrid that shares some Java and C# code...

Xamarin does Android well too :)
If Java developers do fall in love with C# then they also find that Xamarin does Android just as well as anyone (probably better!). Our MonoDevelop IDE (and our integration with Visual Studio) means you get an awesome coding experience (autocomplete/intellisense/code completion, debugging, refactoring, source control integration, etc) as well as a drag-and-drop UI designer. You be the judge of whether it's a better experience than Eclipse :)

Fin
In summary, why not? As a Java developer you have a lot to gain and nothing to lose by giving C# a try. A whole world of new platforms, tools and frameworks become available with a very low barrier to entry. Try it - you just might like it ;-)

Thursday, 3 January 2013

Localizing iOS6 Storyboards with MonoTouch

Localization and internationalization of XIB and Storyboard files has historically been a very manual process. Typically these file types would be duplicated in each 'language directory' (*.lproj) and then the text and layout tweaked independently by each translator. Changes to the actual Storyboard or XIBs would need to be manually propagated across all the 'language copies'.

In iOS6 Apple introduced a new concept - Base Localization - which you can read about in their documentation on Internationalizing Your App. Your project can contain a 'special' Base.lproj directory where the Storyboard files are located, and then a corresponding *.strings file (whose filename matches the Storyboard's) in each language directory with the translations.

There is a small sample showing how this works with MonoTouch - TaskyL10nStoryboard on github. There is no IDE support currently, but you can easily create the Base.lproj directory manually in MonoDevelop and everything works as expected. Here's a screenshot of the project structure:

then inside the Storyboard itself you need to identify the controls you wish to localize. Click on a control to discover its Object ID, and shown in this screenshot:

Using the Object IDs from the Storyboard file, we can translate the display values for various properties (including text, placeholders and others) in the MainStoryboard.strings file in each language directory, like this:
"SXg-TT-IwM.placeholder" = "nombre de la tarea";
"Pqa-aa-ury.placeholder"= "otra información de tarea";
"zwR-D9-hM1.text" = "Detalles de la tarea";
"bAM-2j-Rzw.text" = "Notas";
"NF3-h8-xmR.text" = "Completo";
"MWt-Ya-pMf.normalTitle" = "Guardar";
"IGr-pR-05L.normalTitle" = "Eliminar";
Using the Object ID and property as the localization key is quite different to Apple's previous guidance on localization, where the key is typically the base-language's actual displayable value.

Here's the localized Storyboard, in Japanese:


What about layout?
Strings can often be very different lengths in different languages. When you used a different Storyboard or XIB for each language then the control sizes could be manually adjusted to fit. When using Base Localization you should use Apple's new constraint-based layout to handle these size differences.

What about iOS5 and earlier?
This method only works on iOS6. To localize text on earlier versions of iOS you will have to duplicate your Storyboards and XIBs for each language, or create outlets for all the controls and set their localized text values in code using NSBundle.MainBundle.LocalizedString().
There is another localized sample - TaskyL10n - which shows how to localize text elements directly and uses MonoTouch.Dialog.

Sunday, 23 September 2012

iOS 6 UIRefreshControl with MonoTouch

Another cute little new feature of iOS 6 is the built-in pull-to-refresh control - UIRefreshControl. It is really simple to wire up with Xamarin on a regular UITableViewController or with MonoTouch.Dialog.

In case you're unfamiliar with it, this is how the new control looks:

To implement:

  • Assign the RefreshControl property of a UITableViewController to a new instance of the control, eg. RefreshControl = new UIRefreshControl();
  • Create a handler for the control's ValueChanged event, eg RefreshControl.ValueChanged += HandleValueChanged;. In this handler your code will do whatever is required to refresh the data when the user pulls down enough to trigger the event.
  • In HandleValueChanged code, once you have refreshed the table, call the EndRefreshing method of the control to stop it spinning. Your refresh code was probably not running on the main thread, in which case you'll probably want to use InvokeOnMainThread like this: InvokeOnMainThread (() => {RefreshControl.EndRefreshing (); });

There are additional features you may use:

  • BeginRefreshing - call this method from your code if you start a refresh operation from code (eg. on a timer or in response to some other event). This sets the UI of the control to indicate there is already a refresh in progress.
  • Refreshing - whether there is already a refresh in progress.
  • AttributedTitle - optional text that appears under the refresh control.
  • TintColor - customize the appearance of the control to match your application's theme.

Refer to Apple's UIRefreshControl doc for additional info.

UPDATE: What about iOS 5?

This above example will not work on earlier versions of iOS - the UIRefreshControl class does not exist, nor does the RefreshControl property on UITableViewController. To get around this problem, the following code does a system check and falls back to an old-fashioned navbarbutton in earlier versions of iOS:

if (UIDevice.CurrentDevice.CheckSystemVersion (6,0)) {
    // UIRefreshControl iOS6
    RefreshControl = new UIRefreshControl();
    RefreshControl.ValueChanged += (sender, e) => { Refresh(); };
} else {
    // old style refresh button
    NavigationItem.SetRightBarButtonItem (new UIBarButtonItem (UIBarButtonSystemItem.Refresh), false);
    NavigationItem.RightBarButtonItem.Clicked += (sender, e) => { Refresh(); };
}

The Refresh method should contain the code that actually gets new data and updates the UITableView. That method should contain a similar if (CheckSystemVersion(6,0)) clause that wraps the call to the RefreshControl.EndRefreshing method on the main thread. Users on older operating systems will see this:

Wednesday, 19 September 2012

iOS 6 released, supported by Xamarin

It would be hard to miss the news that Apple is launching their new iPhone 5 this week, and has also released the final version of iOS 6. What's also great is that Xamarin supports iOS 6 too, on release day! There's already plenty of documentation, using C# with StoreKit, PassKit, EventKit, UIKit changes and more.

Of course there are heaps of little additions as well as those big ones, including a raft of new Core Image Filters to play with. For those working on the next Instagram (isn't everyone ;-), here's a preview of a couple of them:


(Posterize, Bloom, Invert, Perspective and Vortex)

For more information, review the Introduction to CoreImage in iOS 5 and the additional sample code for iOS 6. You might also like Apple's CoreImage Filter Reference.

Thursday, 13 September 2012

Microsoft's Azure Mobile Services... and WP7... and Mac

So far, Azure Mobile Services have been added to MonoTouch and Mono for Android (as well as Microsoft's getting started sample for Windows 8).
To complete the 'set', I ported the MonoTouch code to MacOSX using the free, open-source MonoMac AND used @kenegozi's 'unofficial' client to munge Azure Mobile Services into our existing Tasky sample on WP7.

These aren't production-quality implementations, mind you, just a couple of quick hacks to illustrate the beauty and simplicity of having C# and the .NET framework available across all these platforms. Oh, and also show the beauty of Azure Mobile Services :-)
You can grab the code for all of these from TaskCloud/Azure on github. You'll need to sign up for the Azure trial and follow the instructions to set up the Todo list tutorial.
Screenshots
Here's how the WP7 and MonoMac versions look:

Saturday, 1 September 2012

Microsoft's Azure Mobile Services... and Mono-for-Android

Yesterday's post introduced a quick implementation of Microsoft's Azure Mobile Services using MonoTouch to build an iOS app.
The WebClient and Json handling was easily refactored into a single class - AzureWebService - which was then added to the existing Android version of the Tasky sample... and now we have the same Azure Mobile Service being access by three platform clients: Windows 8, iOS and Android all with C# (and the iOS and Android apps sharing the service client code).

Additional features have also been added to AzureWebService to allow deletion of tasks. The Android app source is on github and it looks like this (delete has been added to the iOS app too):

Here is a discussion of how the API was reverse-engineered with Fiddler. The REST endpoints that TaskyAzure accesses are:

GET /tables/TodoItem

GET /tables/TodoItem/?$filter=(id%20eq%20{0})

PATCH /tables/TodoItem/{id}
{"id":1,"text":"updated task text","complete":false}

POST /tables/TodoItem
{"text":"new task text","complete":false}

DELETE /tables/TodoItem/{id}

Finally, only a few small updates were required in the Windows 8 example prevent the completed tasks from disappearing and instead make use of the checkbox in a more natural way:

Now all three apps are reading and writing to the same Azure data store! Can't wait for the official cross-platform APIs :-)

Friday, 31 August 2012

Microsoft's Azure Mobile Services... and MonoTouch

Microsoft only recently announced a cool new addition to the Azure product offering: Mobile Services. They have done a great job at providing a getting started tutorial that gives you a working Windows 8 app in about 5 minutes (seriously, it's fast and easy).

Azure Mobile Services consist of an underlying REST API, so it didn't take long for someone (Chris Risner :-) to put a simple iOS client together. That was all the inspiration required to get it working with MonoTouch.

Actually there is already a MonoTouch todo-list example called Tasky and it has previously been adapted to use Apple's iCloud storage.

The finished code for TaskyAzure borrows heavily from the existing Tasky samples (eg. it uses MonoTouch.Dialog), and really only borrows the REST API urls and Json from Chris' post. I might be biased, but the C# code looks a lot simpler to me :-)
Visit github for the TaskyAzure code. The app looks like this:

And just to prove that the Windows 8 app and the MonoTouch app are both writing to the same Azure database, here is a screenshot of the Azure Management Portal showing the data storage:
Azure Mobile Services looks pretty interesting - look forward to seeing the official cross-platform APIs :-)

UPDATE: to try the code follow the Microsoft's instructions, including creating a free trial account. Once your Azure Mobile Service has been created, configure the app by updating the constants in the AzureWebService.cs class:

static string subdomain = "xxxxxx"; // your azure subdomain
static string MobileServiceAppId = "xxxxxx"; // your application key

Thursday, 23 August 2012

In-app purchase vulnerability & MonoTouch

Earlier this year (around July) Apple announced that a vulnerability had been discovered in the in-app purchasing mechanism, which they discuss here. They provide some Objective-C code which:

  • Checks the information returned matches the information in the SKPayment
  • Checks that new transactions have a unique transaction ID
  • Verifies the SSL certificate
  • Verifies the receipt's signature

I've started a port of that code to MonoTouch, so far it only performs the first two mitigation steps. The VerificationController c# class is a gist.

Adding this code to a simple in-app purchase implementation would turn something like this:

into something like this:

where there is now an additional web request round-trip in your purchase code, plus a pile of comparisons to see whether the the StoreKit receipt matches the one you requested from iTunes directly. It also stores a list of every transaction ID in NSUserDefaults that is used to identify duplicates (which is an indication that the responses are being faked).

WARNING: the c# port currently only performs rudimentary checks comparing the receipt returned by StoreKit to one you've attempted to independently verify with iTunes. Without the SSL certificate and signature checks your code will still be vulnerable to sophisticated hacks on the DNS config to re-route requests and fake the responses. Please consider this a starting point for improving the security of your in-app purchase code.

NOTE: Apple says this vulnerability will be addressed in a future version of the operating system!

Friday, 17 August 2012

Image Metadata with MonoTouch

Developers frequently want to know how to extract metadata from an image file on iOS using MonoTouch. One alternative is to use MonoTouch.ImageIO.CGImageSource to query the image (without even having to load it into memory).

Here is an example on gist that shows how to use the CGImageSource class, and a screenshot of how it looks (you'll have to add your own image to the project):

The key lines of code are:

var imageFilename = "img.jpg";
var url = new NSUrl(imageFilename, false);
CGImageSource myImageSource;
myImageSource = CGImageSource.FromUrl (url, null);
var ns = new NSDictionary();
var imageProperties = myImageSource.CopyProperties(ns, 0);
// Output ALL teh things
//Console.WriteLine(imageProperties.DescriptionInStringsFileFormat);

// Basic Properties
var width = imageProperties[CGImageProperties.PixelWidth];
var height = imageProperties[CGImageProperties.PixelHeight];
var orientation = imageProperties[CGImageProperties.Orientation];
var dimensions = String.Format ("Dimensions: {0}x{1} (orientation {2})", width, height, orientation);
Console.WriteLine(dimensions); 

// TIFF Properties
var tiff = imageProperties.ObjectForKey(CGImageProperties.TIFFDictionary) as NSDictionary;
var make = tiff[CGImageProperties.TIFFMake];
var model = tiff[CGImageProperties.TIFFModel];
var dt = tiff[CGImageProperties.TIFFDateTime];
var tprops = String.Format ("TIFF: {0} {1} {2}", make, model, dt);
Console.WriteLine(tprops); 

// GPS Properties
var gps = imageProperties.ObjectForKey(CGImageProperties.GPSDictionary) as NSDictionary;
var lat = gps[CGImageProperties.GPSLatitude];
var latref = gps[CGImageProperties.GPSLatitudeRef];
var lon = gps[CGImageProperties.GPSLongitude];
var lonref = gps[CGImageProperties.GPSLongitudeRef];
var loc = String.Format ("GPS: {0} {1}, {2} {3}", lat, latref, lon, lonref);
Console.WriteLine(loc); 

// EXIF Properties
var exif = imageProperties.ObjectForKey(CGImageProperties.ExifDictionary) as NSDictionary;
var fn = exif[CGImageProperties.ExifFNumber];
var focal = exif[CGImageProperties.ExifFocalLength];
var eprops = String.Format ("EXIF: Fstop {0} FocalLength {1}", fn, focal);
Console.WriteLine(eprops); 
Xamarin already has a recipe explaining how to save a photo with metadata.

Sunday, 1 April 2012

CoreImage with MonoTouch (example)

In response to a question on the mailing list over the weekend I put together a quick example of using CoreImage in MonoTouch to adjust the Contrast, Saturation and Brightness of an image. It looks like this:
CoreImage with MonoTouch
The code is available in a gist; to use simply create a new MonoTouch Empty iPhone Project and paste in the code/create the files.
It initially uses a supplied image but you can take a photo with the camera and manipulate that instead. NOTE that when you take a camera image, a 'display copy' is made (scaled down to screen resolution) and the Contrast/Saturation/Brightness changes you make are applied to the scaled-down copy to ensure the UI is responsive and avoid memory issues. If you choose to save the image, the C/S/B values are re-applied to the original image before being saved, so you get an 'original resolution' verison saved to the Camera Roll.

There is also a CoreImage project on Xamarin's github which includes examples of many more CIFilters (see also Apple's docs). Plus there's a Sepia-tone example in the MonoTouch iOS5 doc.

FYI using CIFilters with MonoTouch is very simple as this method shows:
// the CoreImage filter  
CIColorControls colorCtrls;
// apply to an image
UIImage AdjustImage (UIImage image) {
   if (colorCtrls == null)
      colorCtrls = new CIColorControls () {
         Image = CIImage.FromCGImage (image.CGImage),
         Brightness = sliderB.Value, 
         Saturation = sliderS.Value, 
         Contrast = sliderC.Value
      };
   else {
      colorCtrls.Brightness = sliderB.Value; 
      colorCtrls.Saturation = sliderS.Value; 
      colorCtrls.Contrast = sliderC.Value;
      colorCtrls.Image = CIImage.FromCGImage(image.CGImage);
   }
   var output = colorCtrls.OutputImage;
   var context = CIContext.FromOptions (null);
   var result = context.CreateCGImage (output, output.Extent);
   colorCtrls = null;
   return UIImage.FromImage(result);
}

Sunday, 4 March 2012

iCloud UIDocument sample: taskcloud

One of iOS5's biggest new features was iCloud storage - the ability for apps to store data (files) "in the cloud" and access them from multiple devices, with OS-supported server-storage/upload, synchronization and backup.
Although it is possible to access "iCloud" functionality directly from within your code, Apple recommends using the new UIDocument class which has a lot of 'built-in' functionality to help manage iCloud documents. At its most basic, you only need to subclass UIDocument and override two methods (LoadFromContents & ContentsForType) to get it working.

The TaskCloud sample creates a TaskDocument subclass and contains some additional code to retrieve a document list from iCloud. The example is a little contrived - using a separate 'file' for each task might not be the best way to model a to-do list application - but you get the idea: individual tasks are created as files and iCloud synchronizes them across all your devices!
The code is still "under construction" so please forgive any dodgy practices. It also doesn't address the full iCloud API, such as conflict resolution or moving files to/from 'local' storage.

You can download the (MonoTouch) code from Github.

As a reminder, here is the guide for configuring your MonoTouch project for iCloud.



(see also, the iCloud Key-Value storage example)

Wednesday, 29 February 2012

iCloud Key-Value example: chat2self

Apple introduced a new API in iOS5 - iCloud Key-Value Data Storage - which lets your applications share a collection of key-value data between devices (with the same App Store login). It's fairly restricted (limited number of keys, limited data size, and no guaranteed time to sync), but it's also very easy to use.

Because the storage is only shared with the same user, it's pretty useless for a chat program. On the other hand, I was keen to play with Miguel's latest BubbleCell example. The result: a simple chat program that only sends messages between your iCloud-registered devices. Each device uses a single 'key' to save the last message you wrote on that device, and iCloud takes care of sync'ing that out to your other devices. When they receive the sync'd data, a notification kicks in and it appears in the conversation! It's definitely half-duplex...


Grab the (MonoTouch) code from Github. Remember: you'd never actually build a chat-service with iOS5 iCloud Key-Value Data Storage, this is just for fun :-)

UPDATE: some handy tips for setting up iCloud for MonoTouch development.



Monday, 18 July 2011

Xamarin welcomes MonoTouch and MonoDroid "home"

Great news! Xamarin has acquired a "broad, perpetual license" to the MonoTouch and Mono-for-Android work that was done while the dev team was part of Novell (prior to the Attachmate buyout kerfuffle). There's a press release on the Novell website and Miguel posted on his blog too. Congratulations to the Xamarin team!

Check out the product details at:
Start with the iOS Roadmap to find out what's coming up next.

Monospace is going to be great! Have you downloaded the app yet :-) ?

Wednesday, 13 July 2011

"M"osetta Stone II: ViewController → Activity

After my recent posts I received a question asking for tips on porting a MonoTouch app to MonoDroid. I'm going to take a crack at that over a couple of days - with the warning that you probably shouldn't rush out and buy either tool but wait for Xamarin's future Mono-on-iOS and Mono-on-Android products.

The purpose of the original "M"osetta Stone post was to give cross-platform developers a 'head start' with some high-level concepts. This post will look a little closer at a screen from the Monospace11 app: the Speakers list.


This may be a massive oversimplification, but...
if you have
___ in iOS
then you want
___ in Android
to
UIViewControllerActivitysubclass to present a screen/form/page to the user
ViewDidLoad()OnCreate()setup the UI
Add() or AddSubview()SetContentView()use in ViewDidLoad/OnCreate to 'attach' UI to display
ViewWillAppear()OnResume()load/re-load data that might change and bind to UI
UITableViewListViewvisual control that presents a scrolling list
UITableViewSourceAdaptersubclass to broker the display of a data collection to the UI
GetCell()GetView()to build the layout(UI) for each cell/row, including re-using cells/views for performance
RowSelected()ItemClick EventHandlerperform an action when a row is touched
RowsInSectionCounttell the table/list how many rows to cater for

Here's how it looks in a Visual Studio Class Diagram*
* I've greyed out some of the methods not relevant to the discussion - the iOS implementation of the alphabetic index down the right edge doesn't have an Android equivalent, for example

Notes:
  • UITableViewSource has been implemented as an nested class to resemble the iOS pattern of implementing the equivalent Obj-C protocols on the ViewController - this pattern makes no sense in Android so the classes are separate.
  • The MonoTouch example code builds the UI in C# (eg. tableView=new UITableView();AddSubview (tableView);) whereas MonoDroid loads layout XML files SetContentView(Resource.Layout.Speakers);. This can happen in both ViewDidLoad/OnCreate and GetCell/GetView where UI controls must be created and presented to the user.
  • The 'touch event handler' is in a different class: in MonoTouch the TableViewSource handles a touch for a specific row (NSIndexPath); MonoDroid implements the ItemClick EventHandler<ItemEventArgs> on the ListView for a given GetRow(e.Position).
  • Both examples use the constructor (TableViewSource/SpeakersAdapter) of the 'broker' class to pass the actual business object collection for display. This pattern lets the data be filtered/sorted/whatever before being passed in for display.
  • Calling refreshSpeakers() from ViewDidLoad/OnResume is kinda redundant in this example since the list never changes - but in other spots (eg the Favorites screen) you would want to refresh the list each time because it may have changed.

The Code
You can have a look at the MonoTouch code SpeakersViewController.cs and the MonoDroid SpeakersActivity.cs, SpeakersAdapter.cs to see the actual implementation. For MonoDroid you'll also need to check out the layout XML files Speakers.axml and SpeakersItem.axml.

One more thing...
One final piece which takes some getting used to... in MonoTouch this screen is part of a UINavigationController stack and your RowSelected creates a new ViewController object (passing whatever parameters you need) and calls NavigationController.PushViewController(). The ViewController instances in the navigation stack are 'stateful' and it is really easy to pass business objects around as parameters.
In MonoDroid, the ever-present 'back-stack' does not require a specific 'navigation controller' BUT at it's simplest the mechanism is much more primative and involves passing simple type parameters (eg. strings) like this
var intent = new Intent();
intent.SetClass(this, typeof(SpeakerActivity));
intent.PutExtra("Name", speaker.Name);
StartActivity(intent);

There are more sophisticated approaches but it is probably best to keep your parameter-passing to a minimum - this way the Android hardware 'Back' button "just works".

HTH Slava ;-)