June 11, 2014

Using Custom UIViewControllers in Xamarin.Forms on iOS

By

Xamarin.Forms abstracts the native controls of Android, iOS, and Windows Phone, allowing developers to create native UIs on each platform from a single, shared C# codebase. This delivers not only reusable user interface code, but also the native user experience that users demand across all platforms.

Sometimes though, we may want to do something that directly uses the platform APIs rather than working at the Xamarin.Forms layer. On iOS we might want to access properties on a CALayer or work directly with a UIViewController.

iospagerenderer

Perhaps we already have existing platform specific UI code that we want to reuse in part of our app, but still want to adopt Xamarin.Forms going forwards as needed. Xamarin.Forms shines in this department and is designed to allow us to tap directly into the native platform through a technology known as renderers.

We can create a renderer to implement our own native UI code at the control level or even at the page level. Say we want to take a UIViewController implementation from Xamarin.iOS and bring it into the world of Xamarin.Forms. This can be achieved by implementing a PageRender in Xamarin.iOS that will be used in our Xamarin.Forms shared UI app logic.

Image Cropping

A lot of photo apps today allow their users to crop images. We are able to accomplish this by using gesture recognizers and Core Graphics to allow the user to touch to drag and pinch to resize a cropping rectangle over a photo. Here is a UIViewController that takes advantage of these platform specific iOS capabilities. (the full code is omitted for brevity):

public partial class CropperViewController : UIViewController
{
  ...
  public override void ViewDidLoad ()
  {
    base.ViewDidLoad ();
    ...
    View.AddSubviews (imageView, cropperView);
    ...

ioscropcrontroller

The Xamarin.iOS implementation involves the following steps which are well-suited to native code:

  1. Draw a transparent overlay on an image
  2. Cut out a rectangle in the overlay
  3. Touch to resize and drag the cropping rectangle
  4. Crop the image to the cropping rectangle

To use this controller in a Xamarin.Forms application, rather than deriving directly from UIViewController, we can derive from PageRenderer. Since PageRenderer derives from UIViewController, all the existing code from the controller’s implementation will work just fine.

public class CropperPageRenderer : PageRenderer
{
  ...
  public override void ViewDidLoad ()
  {
    base.ViewDidLoad ();
    ...
    View.AddSubviews (imageView, cropperView);
    ...

Additionally, we may want to apply model data set on the Xamarin.Forms side into the native code. We can achieve this by implementing OnElementChanged. For example, here we take a string from the Text field in the Xamarin.Forms ContentPage, which is of type CropperPage in this case, and apply it to a native UILabel.

protected override void OnElementChanged (VisualElementChangedEventArgs e)
{
  base.OnElementChanged (e);
  var page = e.NewElement as CropperPage;
  var view = NativeView;
  var label = new UILabel (new RectangleF(20, 40, view.Frame.Width-40, 40));
  label.AdjustsFontSizeToFitWidth = true;
  label.TextColor = UIColor.White;
  if (page != null) {
    label.Text = page.Text;
  }
  view.Add (label);
}

ioscroprenderer1

The Xamarin.Forms class CropperPage will use CropperPageRenderer when we add the following attribute to our custom renderer class:

[assembly:ExportRenderer(typeof(Cropper.CropperPage), typeof(Cropper.iOS.CropperPageRenderer))]

On the Xamarin.Forms side, the CropperPage is a ContentPage.

public class CropperPage : ContentPage
{
    public string Text = "Image cropping in native renderer";
    public CropperPage ()
    {
        BackgroundColor = Color.Purple;
    }
}

Notice the Text field that was accessed from OnModelSet earlier that comes from this class. Also, UI code can be set in the ContentPage and it is included with the code from the renderer. An example of this is the BackgroundColor, which is set to purple and is visible in the renderer after the image is cropped:

ioscroprenderer2.png

So as you can see, blending native code with Xamarin.Forms is very straight forward, offering the flexibility to work from a higher level of abstraction, while always having access to the power of the native platform APIs when you want them.

The code from this post is available in my GitHub repo.

Discuss this blog post in the Xamarin Forums

TwitterFacebookGoogle+LinkedInEmail