Xamarin.Tip – Fixing Missing iOS App Icons

In the age of Xamarin Hot Reload and Hot Restart, we are beginning to get spoiled with the tooling we use every day. I find myself spending less and less time dealing with workarounds for Xamarin issues, filing bug reports, or getting frustrated at not understanding why something isn’t working.

Until today…

The issue – I created an AppIcon for my iOS app, and it simply won’t show up on any device or simulator. I had seen this before, so I double checked everything and tried all my old tricks:

  • Check that the AppIcon asset was in the Assets.xcassets
  • Check that the actual AppIcon had all the images filled in
  • Check that my info.plist was pointing at my AppIcon
  • Clean
  • Rebuild
  • Clear all caches
  • Restart VS
  • Restart machine
  • Try on another machine, repeat above steps
  • Panic

Nothing. So, if you’re new to Xamarin development or iOS development in general, the above steps are definitely the first things to try!

One thing I did differently this time around was use https://makeappicon.com/ to generate my individual icons from a single high resolution image. Usually the designer on my team will send me the exact icons exported to the specific sizes, but they were on vacation and I wanted to make some progress! I 100% recommend using MakeAppIcon for those who haven’t discovered it yet.
For those that haven’t used it, MakeAppIcon gives you a zip with the actual AppIcon.appiconset which includes every size you need and their registration in the Contents.json which tells the build agent what images fit which profile/size. This Contents.json is also what Visual Studio uses to show you the icons in your Assets.xcassets.

So all you have to do is take the AppIcon.appiconset and drop it in your Assets.xcassets and you’re off and running. Or so I thought…

Turns out VS uses the Content.json to read the images to show in your assets, but this does not actually include them in any sort of build output to your final .ipa – so we need to actually update our ios project’s .csproj file to make sure all the images and their full paths are included – not just the AppIcon.appiconset. Normally, if you drag and drop or file select within Visual Studio to set the images for your app icon, this is done for you. But if you bring in your icons from any outside source, be sure to make sure you add them in the .csproj – something like this:

<ImageAsset Include="Assets.xcassets\AppIcons.appiconset\Contents.json">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Contents.json">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-20x20@1x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-20x20@2x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-20x20@3x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-29x29@1x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-29x29@2x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-29x29@3x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-40x40@1x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-40x40@2x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-40x40@3x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-60x60@2x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-20x20@1x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-60x60@3x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-76x76@1x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-76x76@2x.png">
  <Visible>false</Visible>
</ImageAsset>
<ImageAsset Include="Assets.xcassets\AppIcon.appiconset\Icon-App-83.5x83.5@2x.png">
  <Visible>false</Visible>
</ImageAsset>

Long story short – copy paste this into your .csproj if you use MakeAppIcon and save yourself some time. If this didn’t help you or isn’t why your App icon isn’t showing up, here are some links I’ve used in the past:
https://stackoverflow.com/questions/48375681/ios-app-icon-missing-on-ios-11
https://xamarin.github.io/bugzilla-archives/59/59515/bug.html
https://docs.microsoft.com/en-us/xamarin/ios/app-fundamentals/images-icons/app-store-icon
https://blog.rthand.com/post/2017/10/17/app-icons-missing-after-upgrading-to-ios-11-xamarin.aspx


More meaningful and useful content coming soon! Including more Alexa, Dialogflow, Google Assistant, Bixby, Cortana, and of course Xamarin work 🙂


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.

Xamarin.Tip – Build Your Own In-App Developer Options

This post is a continuation of building gesture based “easter eggs” into your Xamarin apps (native or Forms).

In this post, we are going to continue from these posts:
Xamarin.Tip – Add Easter Eggs to UWP with Key Combos
Xamarin.Tip – Add Easter Eggs on Shake

to talk about how we can use these gestures in Debug mode to give ourselves some shortcuts for testing and moving through our application quicker.

To breakdown the process setup – we added shake handlers (and keyboard shortcuts for UWP) that can invoke any contextual code in our application. We talked about using this for funny easter eggs, but this functionality can be made into something super useful for us.

I call this “Contextual Developer Options”. The idea is take that easter egg handler and use it to display some contextual options that can be controlled page by page or view by view. This can be toggling some UI updates, auto-navigating through the app, testing different scenarios, etc.

In this example we’ll be using MVVM and Xamarin.Forms to demonstrate the possibilities, but the same idea can be applied to any other pattern or practice. Basically we need to:

  • Register dev options to whatever context we want
  • Determine what context we are in
  • Display the actionable dev options

Before starting, I suggest looking at something like Allan Ritchie’s incredibly useful UserDialogs package https://github.com/aritchie/userdialogs

We’ll use it in our examples.

Setting Up Developer Options

First things first, let’s define our developer options.

DevelopmentOption.cs

public class DevelopmentOption
{
    public string Title { get; set; }
    public Action Action { get; set; }
}

Now, like I said, our example will be doing this in MVVM and we can use the current page the user is on as the context we want to apply.

I’ll start by creating a BasePageViewModel that we can use to set up our developer options and execute the action

BasePageViewModel.cs

public abstract class BasePageViewModel : ViewModelBase
{
    protected List<DevelopmentOption> _developerOptions;
    private bool _isShowingDeveloperOptions = false;

    public BasePageViewModel()
    {
        _developerOptions = new List<DevelopmentOptions>();
    }

    /// <summary>
    /// Shows the developer options. This should be called after a native easter egg such as a shake.
    /// </summary>
    public virtual async Task ShowDeveloperOptionsAsync()
    {
        if (_developerOptions.Any() && !_isShowingDeveloperOptions)
        {
            var titles = _developerOptions.Select(d => d.Title);
            _isShowingDeveloperOptions = true;
            var actionResult = await UserDialogs.Instance.ActionSheetAsync("Developer Options", "Cancel", null, null, titles.ToArray());
            _isShowingDeveloperOptions = false;
            // see if an option was selected
            if (titles.Contains(actionResult))
            {
                // call it if we find it.
                var option = _developerOptions.FirstOrDefault(d => d.Title == actionResult);
                option?.Action();
            }
        }
    }

    /// <summary>
    /// Sets up the developer options.
    /// </summary>
    protected virtual void SetupDeveloperOptions()
    {
        _developerOptions.Add(new DevelopmentOption()
        {
            Title = "Go to debug page",
            Action = () => UserDialogs.Instance.Toast("Not implemented")
        });
    }

    /// <summary>
    /// Adds the developer option.
    /// </summary>
    /// <param name="title">Title.</param>
    /// <param name="action">Action to perform when selected.</param>
    protected void AddDeveloperOption(string title, Action action)
    {
        _developerOptions.Add(new DevelopmentOption()
        {
            Title = title,
            Action = action
        });
    }
}

Alright let’s breakdown what’s going on here.

Our BasePageViewModel has a List that represents our different options for the given page. Then we have some protected methods we can use in our extended PageViewModels to setup different options.

In our SetupDeveloperOptions method, we can setup any option we want for the page, and we put a universal “Go to debug page” example action that is there on every page.

So here’s an example of a useful ViewModel that sets up a login page developer option.

LoginPageViewModel.cs

public class LoginPageViewModel : BasePageViewModel
{
    public string Email { get; set; }
    public string Password { get; set; }
    protected override void SetupDeveloperOptions()
    {
        AddDeveloperOption("Use test user", () => 
        {
            Email = "mytestuser@mydomain.com";
            Password = "myP@ssw0rd";
        });
    }
}

So what we want on this login page is to be able to shake the device and see the Action Sheet that shows “Use test user” and when selected, it should fill out the fields on the page automatically.

Now that our setup is done, let’s wire up accessing these developer options.

Wiring It Up

We need to be able to access our Page’s ViewModel in order to execute the developer options when the shake happens. To do this, we can access the current page via the Xamarin.Forms.Application.Current static accessor. Then use the MainPage property to get around.

I like to add a simple helper property getter on my App class for this:

App.xaml.cs

public class App : Application
{
    // ...

    // assuming MainPage is a NavigationPage. You can do some extra logic if it isn't
    public ContentPage CurrentPage => (MainPage as NavigationPage)?.Navigation?.NavigationStack?.Last()
    // ...
}

Now in our native app logic, where we were executing the easter egg we can call:

// we don't want developer options in production 🙂
#if DEBUG
await ((App.Current as App)?.CurrentPage?.BindingContext as BasePageViewModel)?.ShowDeveloperOptionsAsync();
#endif

So now if we are in our LoginPage and our BindingContext is set to our LoginPageViewModel, our shake should reveal the action sheet!

Conclusion

We can make our lives a whole lot easier when testing and running through our app by giving ourselves easier and contextual options throughout the entire application.

I’ve personally used this for some pretty awesome and intense stuff like:

  • Filling out forms with different values
  • Changing styles/themes of the app to easily compare 2 different UIs
  • Test different states of a page such as if a user is logged in or not
  • Test error scenarios
  • Build a page that shows logs of all interactions and background tasks in the app and use the developer options to navigate to that page whenever to see those custom logs live (even when not attached to the debugger)
  • Execute custom native code to make debugging easier
  • Navigate quickly to the page deep in the app I am currently working on

Remember to still write unit and UI tests for your app too! This is meant to be something to make real hands-on testing even easier too.

It has saved me hundreds of hours easily. Let me know if you end up implementing something like this too!

For now, I don’t have any complete open source project to demo this, but if enough of you want to see a full example and screenshots and different scenarios, I’ll consider putting a full project together. Let me know in the comments or on twitter!


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.

Xamarin.Tip – Add Easter Eggs to UWP with Key Combos

In a previous post, I talked about adding easter eggs to your iOS and Android applications (using Xamarin.Forms or Xamarin Native) using the shake gesture – allowing us to execute it within the context of the current view we are in whether native or in forms, and then from there execute any bit of code we want!

Check that out here: Xamarin.Tip – Add Easter Eggs on Shake

In this post, I want to give an example of adding the same sort of functionality to your UWP applications. This again is usable whether in Xamarin.Forms or native UWP.

Since nearly all UWP devices are PC’s and not mobile devices, and often don’t have gyrometers, adding this type of feature using a shake gesture just doesn’t make sense. I propose, instead, to use key combos!

Tracking Key Combinations in UWP and Xamarin.Forms

In UWP there are two main things needed to track key combinations – the KeyState and the OnKeyUp method.

Accessing the current KeyState is as easy as:

CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Control);

Where VirtualKey.Control can be any key! In this case it is the Ctrl key.

In our UWP Page classes, we can also override the OnKeyUp method which is fired whenever any key is pressed. This means that in this method, we can check the KeyState of any number of keys, and also get the current key that was just pressed. Alternatively, you can do this in the OnKeyDown override depending on how you want it to behave.

Let’s look at a full example of this implemented where we want to fire some Easter Egg off once the Ctrl + E key combo is hit:

MainPage.xaml.cs

// NOTE: this is the UWP MainPage - not the Xamarin.Forms MainPage!
public sealed partial class MainPage
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    private static bool IsCtrlKeyPressed()
    {
        var ctrlState = CoreWindow.GetForCurrentThread().GetKeyState(VirtualKey.Control);
        return (ctrlState & CoreVirtualKeyStates.Down) == CoreVirtualKeyStates.Down;
    }

    protected async override void OnKeyUp(KeyRoutedEventArgs e)
    {
        base.OnKeyUp(e);
        if (IsCtrlKeyPressed())
        {
            if (e.Key == VirtualKey.E)
            {
                await EasterEggAsync();
            }
        }
    }

    /// <summary>
    /// Do something with an easter egg!
    /// </summary>
    /// <returns>The developer options async.</returns>
    private async Task EasterEggAsync()
    {
        // DO SOMETHING! 😀
        await DoAnEasterEggThing();
    }
}

Just like in our Android and iOS Shake examples, from here in UWP, we can get reference to our current Xamarin.Forms page and execute with some context by hitting (App.Current.MainPage as NavigationPage).CurrentPage assuming that the MainPage of our app is a NavigationPage.

In another post, we will look at combining these 3 platform methods to give ourselves as developers some tools to make our lives easier while debugging and testing!


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.


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.

Xamarin.Tip – Embed Your Xamarin.Forms Pages in Your Android Activities

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.

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 Activity is non-Xamarin.Forms and loads your app right away
  • Init Xamarin.Forms after this activity is loaded
  • Embed Xamarin.Forms pages in other Activities
  • 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 Toolbar 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 Activity that handles the embedding and layout how we want.

xamarin_forms_activity.axml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <android.support.design.widget.AppBarLayout     
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="?android:attr/actionBarSize"
        android:layout_gravity="top"
        app:elevation="0dp">
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?android:attr/actionBarSize"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
    </android.support.design.widget.AppBarLayout>
    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

This layout gives us the native Android toolbar (with the shadow! Another plus!) and a space for us to embed in this FrameLayout.

Now let’s create the Activity:

XamarinFormsActivity.cs

/// <summary>
/// Base xamarin forms activity.
/// This activity contains a single fragment in the layout and renders the fragment pulled from the Xamarin.Forms page
/// </summary>
public abstract class XamarinFormsActivity<TPage> : AppCompatActivity
    where TPage : ContentPage, new()
{
    protected readonly TPage _page;
    protected int _containerLayoutId = Resource.Layout.activity_fragment_container;
    public Android.Support.V7.Widget.Toolbar Toolbar { get; set; }
    public AppBarLayout AppBar { get; set; }

    public XamarinFormsActivity()
    {
        _page = new TPage();
    }

    /// <summary>
    /// Creates the activity and maps the Xamarin.Forms page to the fragment
    /// </summary>
    /// <param name="savedInstanceState">Saved instance state.</param>
    protected override void OnCreate(Android.OS.Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        SetContentView(Resource.Layout.xamarin_forms_activity);

        Toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
        if (Toolbar?.Parent != null)
        {
            AppBar = Toolbar?.Parent as AppBarLayout;
            SetSupportActionBar(Toolbar);
        }

        // register the fragment
        var transaction = SupportFragmentManager.BeginTransaction();
        transaction.Add(Resource.Id.fragment_container, _page.CreateSupportFragment(this));
        transaction.Commit();
        SupportActionBar?.SetDisplayHomeAsUpEnabled(true);
        SupportActionBar?.SetDisplayShowHomeEnabled(true);
        Toolbar?.SetBackgroundColor(Android.Graphics.Color.White);
        // everything else from this point should be managed by the Xamarin.Forms page behind the fragment
    }
}

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 Activity:

SomeActivity.cs

public class SomeActivity : XamarinFormsActivity<SomePage>
{
    protected override void OnCreate(Bundle savedInstance)
    {
        SupportActionBar.Title = "Some Page";
    }
}

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

MainActivity.cs

public class MainActivity : AppCompatActivity 
{    

    protected override void OnCreate(Bundle savedInstance)
    {
        base.OnCreate(savedInstance);
        SetContentView(Resource.Layout.main_activity);
        var someButton = FindViewBy<Button>(Resource.Id.some_button);
        someButton.Click += delegate 
        {
             if(!Xamarin.Forms.Forms.IsInitialized)
                 Xamarin.Forms.Forms.Init(this, savedInstance);
             StartActivity(typeof(SomeActivity));
        }
    } 
}

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 the same setup for iOS, 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.

Xamarin.Tip – No Bounce ScrollView in Xamarin.Forms

So on iOS, UIScrollView bounces by default so in Xamarin.Forms the ScrollView natrually bounces. But what if you don’t want your ScrollView to bounce? Android doesn’t bounce, so we won’t worry about it.

Let’s solve this problem with a custom renderer and control called NoBounceScrollView. This could also be done with an Effect but I like to have a custom control with the verbosity and flexibility with a renderer.

Let’s start by building a simple control in our Xamarin.Forms project:

NoBounceScrollView.cs

public class NoBounceScrollView : ScrollView { }

We don’t need anything in it since we are just going to assume it should never bounce and doesn’t affect the ScrollView in any other way. If you want, you can add a bindable property here to set Bouncable or something like that to true or false. Note: if you don’t want your ScrollView to bounce ever, then you don’t need this. Instead just have your renderer replace the default ScrollView renderer.

So we have our Xamarin.Forms control to build the renderer for, so now let’s create the iOS renderer:

NoBounceScrollViewRenderer.cs

public class NoBounceScrollViewRenderer : ScrollViewRenderer
{

    protected override void OnElementChanged(VisualElementChangedEventArgs e)
    {
        base.OnElementChanged(e);
        UpdateScrollView();
    }

    private void UpdateScrollView()
    {
        ContentInset = new UIKit.UIEdgeInsets(0, 0, 0, 0);
        if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
            ContentInsetAdjustmentBehavior = UIKit.UIScrollViewContentInsetAdjustmentBehavior.Never;
        Bounces = false;
        ScrollIndicatorInsets = new UIKit.UIEdgeInsets(0, 0, 0, 0);
    }
}

What this is doing is setting the content insets to 0 so we don’t have empty space on top or bottom that the bouncing adds, and we also set Bounces to false. Note that the ContentInsetAdjustmentBehavior is only available on iOS 11 and higher, so for that extra step, we need to check the current iOS version.

Lastly, be sure to register your renderer:

[assembly: ExportRenderer(typeof(NoBounceScrollView), typeof(NoBounceScrollViewRenderer))]

or if you are replacing all ScrollViews:

[assembly: ExportRenderer(typeof(ScrollView), typeof(NoBounceScrollViewRenderer))]

Now we have everything we need! So let’s use it in our page:

MainPage.xaml

<ContentPage ...>
    <components:NoBounceScrollView>
        <StackLayout>
            ...
        </StackLayout>
    </components:NoBounceScrollView>
</ContentPage>

Check out the difference!

You can see this control in action with the EF Go Ahead Tours Companion App


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.

Xamarin.Tip – Dynamic Elevation Frames

How about some more Material Design based controls for Xamarin.Forms?

A while back I wrote about creating more “Material” Frames in your Xamarin.Forms apps using a custom renderer for iOS: Xamarin.Tip – Making Your iOS Frame Shadows More Material

And also wrote about the MaterialButton control I created that added dynamic Elevation properties for both iOS and Android:
Xamarin.Tip – Adding Dynamic Elevation to Your Xamarin.Forms Buttons

And I’ve been getting requests to talk about how to do it with the Frame control in Xamarin.Forms. Spoiler Alert: It’s basically the exact same thing as the MaterialButton…..

Let’s start by creating a custom Frame class in our Xamarin.Forms project:

MaterialFrame.cs

public class MaterialFrame : Frame
{
    public static BindableProperty ElevationProperty = BindableProperty.Create(nameof(Elevation), typeof(float), typeof(MaterialButton), 4.0f);

    public float Elevation
    {
        get
        {
            return (float)GetValue(ElevationProperty);
        }
        set
        {
            SetValue(ElevationProperty, value);
        }
    }
}

We add our bindable Elevation property with a default value of 4.

Now we just need simple custom renderers for our iOS and Android implementations.

Starting with iOS:

MaterialFrameRenderer_iOS.cs

public class MaterialFrameRenderer : FrameRenderer
{
    public static void Initialize()
    {
        // empty, but used for beating the linker
    }
    protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
    {
        base.OnElementChanged(e);

        if (e.NewElement == null)
            return;
        UpdateShadow();
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);
        if(e.PropertyName == "Elevation")
        {
            UpdateShadow();
        }
    }

    private void UpdateShadow()
    {

        var materialFrame = (MaterialFrame)Element;

        // Update shadow to match better material design standards of elevation
        Layer.ShadowRadius = materialFrame.Elevation;
        Layer.ShadowColor = UIColor.Gray.CGColor;
        Layer.ShadowOffset = new CGSize(2, 2);
        Layer.ShadowOpacity = 0.80f;
        Layer.ShadowPath = UIBezierPath.FromRect(Layer.Bounds).CGPath;
        Layer.MasksToBounds = false;

    }
}

And now the simple Android renderer that can use the built in Elevation properties

MaterialFrameRenderer_Android.cs

public class MaterialFrameRenderer : Xamarin.Forms.Platform.Android.AppCompat.FrameRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
    {
        base.OnElementChanged(e);
        if (e.NewElement == null)
            return;

        UpdateElevation();
    }


    private void UpdateElevation()
    {
        var materialFrame= (MaterialFrame)Element;

        // we need to reset the StateListAnimator to override the setting of Elevation on touch down and release.
        Control.StateListAnimator = new Android.Animation.StateListAnimator();

        // set the elevation manually
        ViewCompat.SetElevation(this, materialFrame.Elevation);
        ViewCompat.SetElevation(Control, materialFrame.Elevation);
    }

    protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        base.OnElementPropertyChanged(sender, e);
        if(e.PropertyName == "Elevation")
        {
            UpdateElevation();
        }
    }
}

Now with both our renderers, we can reference our MaterialFrame component in our content!

<ContentPage ...>
    <StackLayout>
        <components:MaterialFrame Elevation="6" />
        <components:MaterialFrame Elevation="{Binding SomeNumber}"/>
    </StackLayout>
</ContentPage>

And that’s it! Now you can control the elevation of your frames for both iOS and Android in Xamarin.Forms:
iOSMaterialFrameAndLabel


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.

Xamarin.Tip – Create an Initials Circle View

A while back, I put up a helpful Xamarin.Tip on how to create a simple CircleView in Xamarin.Forms for a BadgeView control – Xamarin.Controls – BadgeView

Since then, I’ve used this simple concept to create some more useful controls that are unique and dynamic in their own way. In this post, we will look at using the CircleView to create an InitialsCircleView. This view is a simple Xamarin.Forms control to show a person’s initials. It is extremely useful in place of user avatars or profile pictures and can add a nice touch to your User Interface without much work at all.

Let’s start by re-iterating how to build a simple CircleView:

In your Xamarin.Forms project, create a new class:

CircleView.cs

public class CircleView : BoxView
{
    public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(double), typeof(CircleView), 0.0);

    public double CornerRadius
    {
        get { return (double)GetValue(CornerRadiusProperty); }
        set { SetValue(CornerRadiusProperty, value); }
    }

    public CircleView()
    {
    }
}

And create your native renderers:

AndroidCircleViewRenderer.cs

public class CircleViewRenderer : BoxRenderer
{
    private float _cornerRadius;
    private RectF _bounds;
    private Path _path;

    public CircleViewRenderer(Context context)
        : base(context)
    {

    }

    protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
    {
        base.OnElementChanged(e);

        if (Element == null)
        {
            return;
        }
        var element = (CircleView)Element;

        _cornerRadius = TypedValue.ApplyDimension(ComplexUnitType.Dip, (float)element.CornerRadius, Context.Resources.DisplayMetrics);

    }

    protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
    {
        base.OnSizeChanged(w, h, oldw, oldh);
        if ((w != oldw && h != oldh) || _bounds == null)
        {
            _bounds = new RectF(0, 0, w, h);
        }

        _path = new Path();
        _path.Reset();
        _path.AddRoundRect(_bounds, _cornerRadius, _cornerRadius, Path.Direction.Cw);
        _path.Close();
    }

    public override void Draw(Canvas canvas)
    {
        canvas.Save();
        canvas.ClipPath(_path);
        base.Draw(canvas);
        canvas.Restore();
    }
}

iOSCircleViewRenderer.cs

public class CircleViewRenderer : BoxRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
    {
        base.OnElementChanged(e);

        if (Element == null)
            return;

        Layer.MasksToBounds = true;
        Layer.CornerRadius = (float)((CircleView)Element).CornerRadius / 2.0f;
    }
}

Okay, cool we can draw circles in Xamarin.Forms easily with

<suave:CircleView .../>

So now let’s build a Xamarin.Forms component on top of this for our InitialsCircleView. The key pieces to making this view unique and cool is:

  • Circle color
  • Font size
  • Font color
  • Font family
  • Circle Radius (in case you don’t really want a circle)

Let’s write the ContentView in XAML, but you could easily do it in C# too:

InitialsCircleView.xaml

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:components="clr-namespace:SuaveControls.Components"
    x:Class="SuaveControls.Components.InitialsCircleView">
    <ContentView.Content>
        <Grid x:Name="Container">
            <components:CircleView x:Name="Circle" Margin="16" HorizontalOptions="Fill" VerticalOptions="Fill" Color="{Binding CircleColor}" CornerRadius="{Binding CornerRadius}"/>
            <Label x:Name="InitialsLabel" VerticalOptions="Center" HorizontalOptions="Center" HorizontalTextAlignment="Center" TextColor="{Binding TextColor}" Font="{Binding Font}" FontSize="{Binding FontSize}" />
        </Grid>
    </ContentView.Content>
</ContentView>

So now we have the layout of our InitialsCirlceView, let’s look at the code behind to see how we apply all these different properties and bind them to this internal view:

InitialsCircleView.xaml.cs

public partial class InitialsCircleView : ContentView
{
    public static BindableProperty CircleColorProperty = BindableProperty.Create(nameof(CircleColor), typeof(Color), typeof(InitialsCircleView), Color.White);
    public static BindableProperty TextColorProperty = BindableProperty.Create(nameof(TextColor), typeof(Color), typeof(InitialsCircleView), Color.White);
    public static BindableProperty FontSizeProperty = BindableProperty.Create(nameof(FontSize), typeof(int), typeof(InitialsCircleView), 14);
    public static BindableProperty FontProperty = BindableProperty.Create(nameof(Font), typeof(Font), typeof(InitialsCircleView), Font.Default);
    public static BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(double), typeof(InitialsCircleView), 0.0, propertyChanged: (bindable, oldVal, newVal) =>
    {
        var initialsView = bindable as InitialsCircleView;
        if (initialsView != null)
            initialsView.UpdateCornerRadius((double)newVal);
    });
    public static BindableProperty NameProperty = BindableProperty.Create(nameof(Name), typeof(string), typeof(InitialsCircleView), string.Empty, 
    propertyChanged: (bindable, oldVal, newVal) =>
    {
        var initialsView = bindable as InitialsCircleView;
        if (initialsView != null)
            initialsView.UpdateTextWithName(newVal?.ToString());
    });

    public double CornerRadius
    {
        get
        {
            return (double)GetValue(CornerRadiusProperty);
        }
        set
        {
            SetValue(CornerRadiusProperty, value);
        }
    }
    public string Name
    {
        get
        {
            return (string)GetValue(NameProperty);
        }
        set
        {
            SetValue(NameProperty, value);
        }
    }
    public int FontSize
    {
        get
        {
            return (int)GetValue(FontSizeProperty);
        }
        set
        {
            SetValue(FontSizeProperty, value);
        }
    }
    public Font Font
    {
        get
        {
            return (Font)GetValue(FontProperty);
        }
        set
        {
            SetValue(FontProperty, value);
        }
    }
    public Color CircleColor
    {
        get
        {
            return (Color)GetValue(CircleColorProperty);
        }
        set
        {
            SetValue(CircleColorProperty, value);
        }
    }
    public Color TextColor
    {
        get
        {
            return (Color)GetValue(TextColorProperty);
        }
        set
        {
            SetValue(TextColorProperty, value);
        }
    }


    public InitialsCircleView()
    {
        InitializeComponent();
        Container.BindingContext = this;
    }

    /// <summary>
    /// Updates the name of the text with.
    /// </summary>
    /// <param name="name">Name.</param>
    private void UpdateTextWithName(string name)
    {
        if (string.IsNullOrEmpty(name))
            return;

        var separateWords = name.Split(' ');
        if(separateWords.Length > 0)
        {
            var initialsArray = separateWords.Select(word => word[0].ToString().ToUpper()).ToArray(); // array of string of initials upper cased
            if(initialsArray.Length > 1)
            {
                // grab the first and last
                initialsArray = new string[2] { initialsArray[0], initialsArray[initialsArray.Length - 1] };
            }
            var initialsString = string.Join(string.Empty, initialsArray);
            InitialsLabel.Text = initialsString;
        }
        else
        {
            InitialsLabel.Text = string.Empty;
        }
    }

    /// <summary>
    /// Updates the corner radius.
    /// </summary>
    /// <param name="radius">Radius.</param>
    private void UpdateCornerRadius(double radius)
    {
        Circle.CornerRadius = radius;
    }
}

We have a few properties that are directly bound to the subviews in the XAML which is facilitated by applying the Container.BindingContext = this;.

We also have a property for Name which invokes the internal UpdateTextWithName method. This in turn takes the name of a person, grabs the initials, and sets the text of the Label to it. So we can then use it by just passing a person or thing’s name and let it figure out the initials naturally. So if we say:

var initialsCirle = new InitialsCirlceView
{
    Name = "Alex Dunn"
}

The output will be “AD” in the cirlce.

Here’s how you can now use it in your XAML:

MainPage.xaml

...
<components:InitialsCircleView 
    CircleColor="Red"
    FontSize="32" 
    Name="Alex Dunn" 
    VerticalOptions="Center" 
    HorizontalOptions="Center" 
    TextColor="White" 
    CornerRadius="90" 
    WidthRequest="120" 
    HeightRequest="120"/>

...

Here’s what it looks like!
InitialsCircle

You can also create XAML Styles for it instead of managing all the colors and font options everywhere you use it.


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.

Xamarin.Tip – Add Easter Eggs on Shake

Here’s a quick and fun tip to add a little flare to your Xamarin applications whether in Xamarin.Forms or Xamarin Native! In a later post, we will also look at how to use the Shake Gesture to create useful tools for yourself as a developer, or for your users! This fun post is the foundation for the more serious stuff and will show you how to use the Shake Recognition to do just about anything you want.

In order to handle shake gestures natively, we have to write some Xamarin Native code, but we will assume you are using Xamarin.Forms. We’ll start with Android.

Android Shake Detection

Android is the more complicated between the two major platforms. The process is basically this:

  • Listen to Accelerometer events
  • Check value changes of the x, y, and z axis
  • Compare deltas against a threshold to determine if motion is a shake
  • Execute the fun stuff!

Let’s do this in the MainActivity since every Xamarin.Forms application needs at least that. If you are using Xamarin Native or a mix, then you may want to implement this elsewhere such as your Application class or abstracted with a shared BaseActvity or add it as a field to the Activities you want it in.

MainActivity.cs

/// <summary>
/// Main Activity and entry point for Xamarin.Forms
/// </summary>
public class MainActivity : Activity, Android.Hardware.ISensorEventListener
{
    #region Shake properties
    bool hasUpdated = false;
    DateTime lastUpdate;
    float lastX = 0.0f;
    float lastY = 0.0f;
    float lastZ = 0.0f;

    const int ShakeDetectionTimeLapse = 250;
    const double ShakeThreshold = 800;
    #endregion


    /// <summary>
    /// Sets up the internal lifecycle for registering views and the shake detection
    /// </summary>
    /// <param name="savedInstanceState">Saved instance state.</param>
    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        // all the other set up stuff here

        // shake management set up
        // Register this as a listener with the underlying service.
        var sensorManager = GetSystemService(SensorService) as Android.Hardware.SensorManager;
        var sensor = sensorManager.GetDefaultSensor(Android.Hardware.SensorType.Accelerometer);
        sensorManager.RegisterListener(this, sensor, Android.Hardware.SensorDelay.Game);

    }


    #region Android.Hardware.ISensorEventListener implementation

    /// <summary>
    /// Handles when the sensor range changes
    /// </summary>
    /// <param name="sensor">Sensor.</param>
    /// <param name="accuracy">Accuracy.</param>
    public void OnAccuracyChanged(Android.Hardware.Sensor sensor, Android.Hardware.SensorStatus accuracy)
    {
    }

    /// <summary>
    /// Detects sensor changes and is set up to listen for shakes.
    /// </summary>
    /// <param name="e">E.</param>
    public async void OnSensorChanged(Android.Hardware.SensorEvent e)
    {
        if (e.Sensor.Type == Android.Hardware.SensorType.Accelerometer)
        {
            var x = e.Values[0];
            var y = e.Values[1];
            var z = e.Values[2];

            // use to check against last time it was called so we don't register every delta
            var currentTime = DateTime.Now;
            if (hasUpdated == false)
            {
                hasUpdated = true;
                lastUpdate = currentTime;
                lastX = x;
                lastY = y;
                lastZ = z;
            }
            else
            {
                if ((currentTime - lastUpdate).TotalMilliseconds > ShakeDetectionTimeLapse)
                {
                    var diffTime = (float)(currentTime - lastUpdate).TotalMilliseconds;
                    lastUpdate = currentTime;
                    var total = x + y + z - lastX - lastY - lastZ;
                    var speed = Math.Abs(total) / diffTime * 10000;

                    if (speed > ShakeThreshold)
                    {
                        // We have a shake folks!
                        await EasterEggAsync();
                    }

                    lastX = x;
                    lastY = y;
                    lastZ = z;
                }
            }
        }
    }

    /// <summary>
    /// Execute the easter egg async.
    /// </summary>
    protected virtual async Task EasterEggAsync()
    {
        // HEY OVER HERE! DO SOMETHING COOL!
    }
#endregion

}

Now all you have to do is add whatever your easter egg logic is in the EasterEggAsync method! In this method, you can also track what the current Xamarin.Forms Page is visible by tracking the App.Current.MainPage to add some context to the page your in.

Now let’s look at the iOS implementation.

iOS Shake Detection

iOS makes this process a whole lot easier. We don’t have to do any crazy accelerometer calculations, state tracking, or any of the gross stuff above for Android. Instead, we simply use the built in Gesture API from UIKit and register that we are listening to these types of gestures in the AppDelegate:

AppDelegate.cs

[Register("AppDelegate")]
public class AppDelegate : UIApplicationDelegate
{

    public override UIWindow Window { get; set; }
    public static AppDelegate Current { get; private set; }


    public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
    {
        // Allow shake detection
        UIApplication.SharedApplication.ApplicationSupportsShakeToEdit = true;

        // make this more accessible
        Current = this;
        Forms.Init();

        // we did it! the app is launched!
        return true;
    }
}

Now that we have registered with the UIApplication.SharedApplication.ApplicationSupportsShakeToEdit, we can start overriding the gesture recognizer. If you’re using Xamarin.Forms, you can do this right in the AppDelegate but if you’re not, then you can use this override method in any ViewController or even any sub-UIView class!

/// <summary>
/// Handles when a general motion has ended on the view controller. 
/// We use this to detect the shake of the phone to execute and easter egg
/// </summary>
/// <param name="motion">Motion.</param>
/// <param name="evt">Evt.</param>
public override async void MotionEnded(UIEventSubtype motion, UIEvent evt)
{
    Console.WriteLine("Motion detected");
    if (motion == UIEventSubtype.MotionShake)
    {
        Console.WriteLine("and was a shake");

        await EasterEggAsync();
    }
}

/// <summary>
/// Execute the easter egg async.
/// </summary>
protected virtual async Task EasterEggAsync()
{
    // HEY OVER HERE! DO SOMETHING COOL!
}

// if you're in the view controller then you also need to:

public override bool CanBecomeFirstResponder
{
    get
    {
        return true;
    }
}

/// <summary>
/// Enables the first responder set to allow for shake detection
/// </summary>
/// <param name="animated">If set to <c>true</c> animated.</param>
public override void ViewWillAppear(bool animated)
{
    base.ViewWillAppear(animated);
    this.BecomeFirstResponder();
    // .. other logic
}

Doing this enabled the gesture to be picked up at the application level or in the current UIViewController. You need to ensure the CanBecomeFirstResponder is true and that you call BecomeFirstResponder() on appearing so that the MotionEnded override can be called.

Once again, just fill in the EasterEggAsync method with whatever easter egg you want to invoke!

Conclusion

Easter eggs can make a neat experience in your mobile applications, but using this shake gesture can also help make debugging easier as well as give a QA team the chance to quickly move through stories in the app. In a future post, we will talk more about these possibilities and the benefits of building testing and development options into your apps for testing rather than user-focused and fun easter eggs.


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.

Xamarin.Tip – Expanding ViewCells

Here’s another helpful Xamarin tip to allow you to expand your ViewCells – for example when the user taps a ViewCell, expand it to reveal more information.
Take this gif as an example:

ExpandingCell

There are 3 major parts to making a feature like this work:
1. Bind visibility to the hidden parts of your cell
2. Handle updating that visibility on selection or tap
3. Force the ViewCell to remeasure its size to show the hidden bits.

Let’s first create a new class called ExpandingViewCell:

ExpandingViewCell.cs

/// <summary>
/// Expanding view cell.
/// </summary>
public class ExpandingViewCell : ViewCell
{
    protected override void OnTapped()
    {
        base.OnTapped();
        ForceUpdateSize();
    }
}

Yeah it’s literally that stupid easy. Override OnTapped and call ForceUpdateSize(). However, it’s important to note that this is so easy because of the order – calling the base.OnTapped() first allows for your click / select events to fire off before measuring the size. This means that if we bind an update to show / hide certain controls on tap, those will show first, then we remeasure to make sure they fit.

Here’s the XAML for the ViewCell shown in the gif above:

<components:ExpandingViewCell>
    <StackLayout Orientation="Vertical" Spacing="4" Padding="64,8,16,8" Margin="0">
        <Label Text="{Binding DateLabel}" IsVisible="{Binding ShowDate}" Style="{DynamicResource BodySecondary}" HorizontalOptions="End" HorizontalTextAlignment="End" />
        <Frame CornerRadius="12" Padding="12" Margin="0" BackgroundColor="{Binding MessageColor, Converter={StaticResource ColorConverter}}" VerticalOptions="Center" HorizontalOptions="EndAndExpand" effects:LongPressedEffect.Command="{Binding Path=BindingContext.MessageOptionsCommand,Source={x:Reference Page}}" effects:LongPressedEffect.CommandParameter="{Binding .}">
            <Frame.Effects>
                <effects:LongPressedEffect />
            </Frame.Effects>
            <Label Text="{Binding Content}" Style="{DynamicResource BodySecondary}" />
        </Frame>
        <Label Text="{Binding SentDateLabel}" IsVisible="{Binding IsSentDateVisible}" Style="{DynamicResource BodySecondary}" HorizontalOptions="End" HorizontalTextAlignment="End" />
    </StackLayout>
</components:ExpandingViewCell>

There is a lot going on in this cell (it’s a chat page after all, so it’s a feature rich cell). I even have some long press handlers from my LongPressedEffect that you can read about here: Xamarin.Tip – Xamarin.Forms Long Press Effect

The important pieces to note are the IsVisible="{Binding IsSentDateVisible}" to show the date label or not.

Now in order to update this, we need to bind our SelectedItem in our ListView.

Here’s an example from the gif:

<ListView x:Name="ChatListView" ItemsSource="{Binding Messages}" SelectedItem="{Binding SelectedMessage, Mode=TwoWay}" HasUnevenRows="True" SeparatorVisibility="None" ItemTemplate="{StaticResource BubbleSelector}" />

We use the DataTemplateSelector to choose between the sent bubble and the received bubble, but that isn’t important. If you want to read more about that, check out this doc from Xamarin: https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/data-templates/selector

Now lastly, let’s look at what’s going on in the ViewModel behind the ListView and how we are binding the SelectedItem.

ChatPageViewModel.cs

...

public ChatMessageViewModel SelectedMessage
{
    get
    {
        return _selectedMessage;
    }
    set
    {
        Set(ref _selectedMessage, value);
        if (value != null)
        {
            ToggleDetails(value);
            SelectedMessage = null;
        }
    }
}

/// <summary>
/// Toggles the details of the given message.
/// </summary>
/// <param name="message">The message view model.</param>
private void ToggleDetails(ChatMessageViewModel message)
{
    message.ShowSenderName = !message.ShowSenderName;
    message.IsSentDateVisible = !message.IsSentDateVisible;
}

...

When the SelectedMessage we bound is changed, and not null, we toggle the ShowSenderName and the IsSentDateVisible to show/hide those views.

So the process looks like this:
– ViewCell is tapped
– ViewCell base call
– SelectedItem toggled
– SelectedMessage toggles the ViewModel properties
– Two way binding triggers to show / hide the inner views
– ViewCell ForceUpdateSize called
– ViewCell expands or collapses

And once again, we get a nice little effect like this!

ExpandingCell


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.