Building a “Next Entry” Effect for iOS and Android in Xamarin.Forms

Update June 26th: Eagle eyed commenter @Brandon Minnick pointed out that ReturnType and ReturnCommand are now supported properties in Xamarin.Forms 3.1 (Documentation). You can find his comprehensive sample here or an amended sample below.


<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms&quot;
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml&quot;
x:Class="samples.core.Views.EntryMoveNextView"
xmlns:ctrl="clr-namespace:samples.core.Controls;assembly=samples.core">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Name="MyEntry" TargetType="Entry">
<Setter Property="HeightRequest" Value="45" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout Padding="15,25">
<Entry x:Name="Entry_First" ReturnType="Next" Placeholder="First Entry" HeightRequest="45"/>
<Entry x:Name="Entry_Second" ReturnType="Next" Placeholder="Second Entry" HeightRequest="45"/>
<Entry x:Name="Entry_Third" ReturnType="Done" Placeholder="Third Entry" HeightRequest="45"/>
<BoxView/>
<Button Text="Save" VerticalOptions="End"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>

And the code behind:


using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace samples.core.Views
{
public partial class EntryMoveNextView : ContentPage
{
public EntryMoveNextView()
{
InitializeComponent();
Entry_First.ReturnCommand = new Command(() => Entry_Second.Focus());
Entry_Second.ReturnCommand = new Command(() => Entry_Third.Focus());
}
}
}

Of course, if you are using an older version of Xamarin.Forms or just want to do it yourself, my original post follows:


Almost every Xamarin.Forms mobile build I have done has required a “form” to be presented to the user. Whether it be for creating a new item in a task list, registering a new user, or other function, a point of frustration while constructing these views was there was no obvious way of setting focus to the next input field on the screen. Without it, users would hit the return key (dismissing the keyboard), and they would be required to tap the next field to proceed or simply hope the on screen keyboard didn’t cover the next field so that the user could proceed manually… a frustrating and inefficient UX to say the least. To circumvent this, we’d need to implement a few key features, we just need a few extra steps to make our form complete and make our UX much more fluid.  While this is fairly obvious when dealing with native development, the means of achieving it in Xamarin.Froms is not so plain. But, with a few simple components, we can configure our Xamarin.Forms Entry fields to specific the next field in the form and make things so much easier for the user. We’ll need a custom control to leverage in our Xamarin.Forms as well as some new effects to make it happen.

Custom Control

Custom controls are a very powerful way of of combining and configuring out-of-the-box Xamarin Components to suit your needs. With a little xaml and a little know-how, we can combine and customize existing tools to suit our needs and easily re-use them throughout a project. In our case, we just need to extend the existing Entry control ever so slightly so that we may specify the next Entry in the form. Take a look:


using System;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
namespace samples.core.Controls
{
public class EntryMoveNextControl : Entry
{
public static readonly BindableProperty NextEntryProperty = BindableProperty.Create(nameof(NextEntry), typeof(View), typeof(Entry));
public View NextEntry
{
get => (View)GetValue(NextEntryProperty);
set => SetValue(NextEntryProperty, value);
}
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
this.Completed += (sender, e) =>
{
this.OnNext();
};
}
public void OnNext()
{
NextEntry?.Focus();
}
}
}

The key takeaways here: we’ve slightly extended the basic Entry control to add a new bindable property so we may specify the next entry in line and created a simple method for setting the focus to the value of that property when called. We can now deploy this in our xaml, like so:


<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms&quot;
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml&quot;
x:Class="samples.core.Views.EntryMoveNextView"
xmlns:ctrl="clr-namespace:samples.core.Controls;assembly=samples.core">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Name="MyEntry" TargetType="Entry">
<Setter Property="HeightRequest" Value="45" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout Padding="15,25">
<ctrl:EntryMoveNextControl x:Name="Entry_First" Placeholder="First Entry" HeightRequest="45"/>
<ctrl:EntryMoveNextControl x:Name="Entry_Second" Placeholder="Second Entry" HeightRequest="45" Keyboard="Numeric"/>
<ctrl:EntryMoveNextControl x:Name="Entry_Third" Placeholder="Third Entry" HeightRequest="45" Keyboard="Numeric"/>
<BoxView/>
<Button Text="Save" VerticalOptions="End"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>

Personally, I find it easier to set the NextEntry in code behind, but you could realistically do everything all in the xaml. For reference however, here is my xaml.cs:

namespace samples.core.Views
{
    public partial class EntryMoveNextView : ContentPage
    {
        public EntryMoveNextView()
        {
            InitializeComponent();

            Entry_First.NextEntry = Entry_Second;
            Entry_Second.NextEntry = Entry_Third;
        }
    }
}

At this point, (on iOS, at least) we’ve actually got a pretty good representation of the functionality we want: so long as we set a value for our NextEntry property, we can direct focus to the next item.

Jun-24-2018 14-15-23

That said, with just a little more effort, we can customize the keyboard to better indicate to the user what will happen when they hit “return”. We can achieve this with a simple effect per platforms

Android Effect

Our Android Effect will look like so:


using System;
using samples.core.Controls;
using samples.Droid.Effects;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportEffect(typeof(EntryMoveNextEffect), nameof(EntryMoveNextEffect))]
namespace samples.Droid.Effects
{
public class EntryMoveNextEffect : PlatformEffect
{
protected override void OnAttached()
{
// Check if the attached element is of the expected type and has the NextEntry
// property set. if so, configure the keyboard to indicate there is another entry
// in the form and the dismiss action to focus on the next entry
if (base.Element is EntryMoveNextControl xfControl && xfControl.NextEntry != null)
{
var entry = (Android.Widget.EditText)Control;
entry.ImeOptions = Android.Views.InputMethods.ImeAction.Next;
entry.EditorAction += (sender, args) =>
{
xfControl.OnNext();
};
}
}
protected override void OnDetached()
{
// Intentionally empty
}
}
}

With a simple check, we can determine if we should show a “Next” key on place of the return key, which makes it much more obvious to the user that they will advance through the form.

iOS Effect

Let’s apply the same line of reasoning to iOS:


using System;
using CoreGraphics;
using samples.core.Controls;
using samples.iOS.Effects;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportEffect(typeof(EntryMoveNextEffect), nameof(EntryMoveNextEffect))]
namespace samples.iOS.Effects
{
public class EntryMoveNextEffect : PlatformEffect
{
protected override void OnAttached()
{
var entry = (UITextField)Control;
// Change return key to Don key
entry.ReturnKeyType = UIReturnKeyType.Done;
// Add the "done" button to the toolbar easily dismiss the keyboard
// when it may not have a return key
if (entry.KeyboardType == UIKeyboardType.DecimalPad ||
entry.KeyboardType == UIKeyboardType.NumberPad)
{
entry.InputAccessoryView = BuildDismiss();
}
// Check if the attached element is of the expected type and has the NextEntry
// property set. if so, configure the keyboard to indicate there is another entry
// in the form and the dismiss action to focus on the next entry
if (base.Element is EntryMoveNextControl xfControl && xfControl.NextEntry != null)
{
entry.ReturnKeyType = UIReturnKeyType.Next;
}
}
protected override void OnDetached()
{
// Intentionally empty
}
private UIToolbar BuildDismiss()
{
var toolbar = new UIToolbar(new CGRect(0.0f, 0.0f, Control.Frame.Size.Width, 44.0f));
// Set default state of buttonAction/appearance
UIBarButtonItem buttonAction = new UIBarButtonItem(UIBarButtonSystemItem.Done, delegate { Control.ResignFirstResponder(); });
// If we have a next element, swap out the default state for "Next"
if (base.Element is EntryMoveNextControl xfControl && xfControl.NextEntry != null)
{
buttonAction = new UIBarButtonItem("Next", UIBarButtonItemStyle.Plain, delegate
{
xfControl.OnNext();
});
}
toolbar.Items = new[]
{
new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace),
buttonAction,
};
return toolbar;
}
}
}

Note that we have a little extra logic on this side of the house: In some keyboard configurations (such as numeric), there is no return button, so we need to customize the keyboard with a toolbar. In fact,  if it makes more sense for a given application, we could show the toolbar for every keyboard type, which would give us more power to expose functionality like adding a “previous” button. We’ll stick with a simple implementation in this case, and save the advanced configuration for another time 😉. We now need to apply our effect to the appropriate page xaml.cs, like so:

Entry_Second.Effects.Add(Effect.Resolve("Effects.EntryMoveNextEffect"));

Here is what the result would look like in either keyboard configuration:

And thats a wrap! This example and others can be found on my GitHub

9 thoughts on “Building a “Next Entry” Effect for iOS and Android in Xamarin.Forms

    1. Wow! That’s amazing, thanks for pointing that out – 3.1 has so many cool features, I can barely keep up 🙂 I’ve updated the post to direct folks to the proper channels and your superb example

      Like

  1. Hey,
    I tried out the updated solution, but the “Next” command still doesn’t work completely on Android. It changes the keyboard button but doesn’t really help to go on to the next text field after editing.

    Wiring up Completed to call the next entry’s “Focus()” works, kinda, but the keyboard gets hidden and re-shown between the fields. Which isn’t pretty. There is an existing issue regarding this in xamairnForms (https://github.com/xamarin/Xamarin.Forms/issues/4214)

    Like

  2. Great example! Though is there something that changed recently with the iOS side of things? I cannot get the toolbar to pop up for iOS. It doesn’t seem like the code is running at all either… I tried following what you had to the best I could. Any suggestions?

    Liked by 1 person

    1. Good find Brett! I apparently had some sloppy commits and forgot to push some files. I’ve updated the repo with both the out of the box sample introduced in 3.1 and my custom solution, which included the modified toolbar with next button. Two things I had edited out of this post and my repo: the effect is only applied for certain keyboard types and the effect needs to be added to any (custom) entry you want it to appear on. This gist should highlight the major changes, and the repo should be updated to run as-is now. https://gist.github.com/JoeM-RP/3d0bd6f2275f34524e0ddafb23490867
      I appreciate the heads up things were broken! Let me know if you run into any other issues

      Like

Leave a reply to Joe Meyer Cancel reply