Xamarin.Tip – Embed Your Xamarin.Forms Pages in Your iOS ViewControllers

The number one complaint I hear about Xamarin.Forms is the slow startup time of the applications that use it. The team at Xamarin has done a lot to help with this and give some more options such as XAML Compilation, Lazy Loading, and Ahead of Time Compilation. Check out some of David Ortinau’s suggestions here: 5 Ways to Boost Xamarin.Forms App Startup Time.

However, one of the best ways I’ve found to help with this issue is to use Xamarin.Forms Embedding to its full potential. Embedding became available in Xamarin.Forms 2.5 and at a high level allows you to embed your Xamarin.Forms pages into your Native Xamarin views by using the extension methods .CreateFragment(Context context); for Android and .CreateViewController(); for iOS. This is most commonly used for when you want to share some UI in your Xamarin Native apps using Xamarin.Forms, however you still need to call Xamarin.Forms.Init() which is one of the main culprits in the slow startup time.

For Android embedding, see: Xamarin.Tip – Embed Your Xamarin.Forms Pages in Your Android Activities

iOS

The solution proposed here still allows you to create almost all of your views in Xamarin.Forms by using embedding, but requires some architecture and design changes. The premise is this:

  • First ViewController is non-Xamarin.Forms and loads your app right away
  • Init Xamarin.Forms after this ViewController is loaded
  • Embed Xamarin.Forms pages in other ViewControllers
  • Lift navigation out of Xamarin.Forms and into the native navigation.

This also has advantages outside the startup time such as better performance on transitions, more natural look and feel to end-users, performance gains in other areas, and a smaller app-size.

This means:

  • No NavigationPage
  • No Xamarin.Forms toolbar (using the native UINavigationBar control instead)
  • Still have MVVM and all our bindings we would expect

So if you’re already using a framework that is not tied down to Xamarin.Forms such as MvvmLight, you don’t have to change much behind the scenes since the INavigationService is abstracted.

Let’s kick this off by creating an inheritable ViewController that handles the embedding and layout how we want. Be sure to use your Storyboard and have the RootViewController be a UINavigationController, then use this embeddable ViewController within that.

XamarinFormsViewController

/// <summary>
/// Base xamarin forms view controller. Used for embedding a Xamarin.Forms page within a native view controller.
/// When inheriting from this, be sure to create a ViewController within the storyboard as well so that navigation
/// can properly work.
/// </summary>
public abstract class XamarinFormsViewController<TPage> : UIViewController
    where TPage : ContentPage, new()
{
    protected TPage _page;


    public XamarinFormsViewController(IntPtr handle) : base(handle)
    {
    }

    /// <summary>
    /// Load the Xamarin.Forms Page's ViewController into the parent
    /// </summary>
    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        _page = new TPage();
        var xamarinFormsController = _page.CreateViewController();
        AddChildViewController(xamarinFormsController);
        View.AddSubview(xamarinFormsController.View);
        xamarinFormsController.DidMoveToParentViewController(this);

        // add whatever other settings you want - ex:
        EdgesForExtendedLayout = UIKit.UIRectEdge.None;
        ExtendedLayoutIncludesOpaqueBars = false;
        AutomaticallyAdjustsScrollViewInsets = false;

    }
}

When creating a child of this XamarinFormsViewController, be sure to also create an empty ViewController in your .storyboard file for each unique type. This is required for handling navigation using the storyboard and root UINavigationViewController. If you’re using .xib files for some reason, then don’t worry about it, just instantiate the XamarinFormsViewController itself (you’ll have to add the other constructor overloads though).

So now we can create a simple Xamarin.Forms page:

SomePage.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"     x:Class="MyApp.Pages.SomePage">
    <ContentPage.Content>
        <Grid>
            <Label Text="I'm Embedded!" HorizontalOptions="Center" VerticalOptions="Center"/>
        </Grid>
    </ContentPage.Content>
</ContentPage>

Then create the associated ViewController:

SomeViewController.cs

public class SomeViewController: XamarinFormsViewController<SomePage>
{
    protected void ViewDidLoad()
    {
        base.ViewDidLoad();

        NavigationItem.Title = "Some title";
    }
}

Now all we have to do is kick off this SomeViewController after calling Xamarin.Forms.Init() and we are good to go! If we have a MainController we can call it before navigating if it isn’t initialized, or execute it in ViewDidLoad or a similar lifecycle event.

MainController.cs

public class MainController: UIViewController
{    

    protected override void ViewDidLoad()
    {
        base.ViewDidLoad();

        // assume SomeButton is created and named in the Storyboard file
        SomeButton.TouchUpInside += delegate 
        {
             if(!Xamarin.Forms.Forms.IsInitialized)
                 Xamarin.Forms.Forms.Init(this, savedInstance);

             var someController = this.Storyboard.InstantiateViewController("SomeController") as SomeViewController;
             NavigationController.PushViewController(someController, true);
        }
    } 
}

And there you have it! Some new Xamarin.Forms embedding for performance and other extra benefits 🙂

In future posts of this subject, we’ll look at extending interactions between the Xamarin.Forms Page and the native Activity and ViewControllers, using advanced native components with the embedded Xamarin.Forms Page, and more!

Let me know what you think of this pattern – have you used it? What else would you want to hear about it??

Be sure to checkout some of the Xamarin examples on embedding too!


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

15 thoughts on “Xamarin.Tip – Embed Your Xamarin.Forms Pages in Your iOS ViewControllers”

  1. Hey Alex, thanks for the nice article!
    I’ve one question I’m wondering about. With this approach you wouldn’t be able to load your usual Xamarin.Forms.Application class since you can’t use the FormsApplicationDelegate, right? This means you wouldn’t be able to create global styles, colors etc. for the forms pages or is there another way to achieve that?

    Like

    1. Yep! No Xamarin.Forms.Application! 😮

      There are definitely other ways to get that though! Here are 2 that I have personally used with this approach:

      – Create separate ResourceDictionaries for your different resource types
      – Create a BaseBindableContentPage that all your embedded pages inherit from
      – in the ctor of the base page, add the resources

      OR
      – Add them to the page in the BaseXamarinFormsActivity and BaseXamarinFormsViewController after creating the page.
      – So, in ViewDidLoad and OnCreate:
      – _page = new TPage(); _page.Resources.Add(….)

      Like

  2. Hi Alex,

    I think this is an interesting post. My pessimistic thoughts with these types of approaches to Xamarin are that they are best suited to small projects, at first. Because you never quite know when something major is going to break in an approach like this. I’m a bit unsure about the navigation or lifecycle aspects here, but I’m no expert when it comes to how XF or iOS is going to handle this. I will try it out in projects of my own. The fast startup is an exciting prospect.

    Also, is the use of the word of Activity in relation to iOS common? I thought Activities were Android specific?

    Like

    1. Hey Charlie, thanks for the questions. I actually prefer this project for scalability reasons in apps that require some heavy UI lifting or faster startup, since the bigger a Forms app gets, the slower the startup can be and the bigger the app size gets when introducing tons of custom renderers or third party tools when trying to step outside the OOTB controls.

      I’ve used this approach on 3 enterprise-level applications (~80-100 screens/pages) and it’s been great! There are some more design patterns that, when implemented on top of this, make the development process incredibly simple and still flexible. I’m going to be sharing some more of those little things in future posts on this subject as mentioned at the end of the post.

      Like

    2. I should also mention that lifecycle is just taken into the hands of the native lifecycle which is actually a nice plus since they are different between the different platforms and both have more events to hook into than the Xamarin.Forms.Application class does.

      As for navigation, I use an abstraction from either myself or something like MvvmLight which has an abstracted INavigation implementation for the native platforms. Also means no ugly transitions for Android 🙂

      Like

  3. very interesting. i’ve found one of the biggest performance improvements is by bypassing xaml all together and doing coded UI – plus that allows for alot of use of inheritance which is great. I find the startup on iOS doing that to be very acceptable, its only android that still leaves much to be desired – but coded UI is still faster on android than xaml!

    Like

    1. Definitely! Or at least compiling the XAML. But like you said, Android is still incredibly slow at startup which is why I went for this approach with a couple apps that needed a snappy startup.

      I’m trying my hand at elmish style now too which just takes coded UI to the next level of clean and manageable 🙂

      Like

  4. I have done something similar recently in a classic project. However I inherit from FormsAppCompatActivity on Android and I use NavigationPage on both iOS/Android successfully with some tweaks to integrate a custom Toolbar (custom font and alignment) on Android. I´m also using RxUI and a custom ViewModel-first navigation and dependency injection using reflection. At the end I have 2 types of navigation, the native one (which will open the activity/view controller) and xamarin.forms navigation to push/pop pages on a NavigationPage.

    As commented here, the big downside on performance is the XAML, even using XAMLC. The exact moment of the loading delay is on InitializeComponent on the App.xaml.cs. That´s a lot faster without coded views.

    Like

    1. That’s basically the setup I had, but without the Xamarin.Forms navigation, just using an abstracted injected INavigationService from my ViewModel. I’ve done this with MvvmLight and RxUI and both have been awesome! Glad to hear I’m not alone! Also true on the coded UI vs Xaml UI – that’s a trade off of performance vs development preference and speed (if you like markup)

      Like

      1. I had problems (null object references to things on screen like simple buttons) when navigating back if I don´t inherit from FormsAppCompatActivity on Android. Did you have those? However that does not happen on iOS. For the codedUI vs Xaml. I don´t get why InititializeComponent is so slow if we use XamlC. I thought that meant to be compile time, not runtime at all. I prefer Xaml cause it´s easier to read and maintain, but we are considering making the initial forms view with code, and the rest (navigation.push) with xaml, just to eliminate this annoying delay.

        Like

      2. I never ran into issues like that, I wonder if you had those from the mix of full xamarin.forms and embedding. With the strategy from this blog post, with only embedding and native nav, it all worked perfectly.

        Like

  5. I was sure that embedding XF in native code could be done in iOS, as I had managed it in Android, but it took me a while to find a good solution. This was perfect, thanks.

    Like

    1. I will say though that in addition to your solution, I ended up with the child view controller only taking up the left half of the screen, which I found strange to say the least. After a few searches it was easily fixed by overriding the following method in the ViewController after ViewDidLoad()

      public override void ViewDidLayoutSubviews()
      {
      base.ViewDidLayoutSubviews();
      myContentPage.View.Frame = new CoreGraphics.CGRect(0, 0, this.View.Bounds.Size.Width, this.View.Bounds.Size.Height);
      }

      Like

Leave a comment