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.