Touchscreen Visualisation on Xamarin iOS

Seems to Have an Invisible Touch

To produce a demonstration video on Android you can turn on screen visualisations from the Developer settings menu and this will shows screen interactions on a screen capture. No such option exists on iOS so you need to write some custom code in your app instead. A couple of solutions exist for native iOS app but to use these from Xamarin would require additional code to wrap. The alternative is to write the code from C# to achieve the same result.

Wrap or Write

My requirements were quite simple, for a first version I only needed to show single touch events and I wanted something which looked visually similar to the Android equivalent. It turns out that the solution is to inherit from UIWindow and add some extra code to capture touch events and draw to the screen. This means a single solution works anywhere in your app as a single Window is used regardless of what views you have. This should work in Xamarin Forms on iOS too by adding similar code to the FinishedLaunching method in your AppDelegate.

When you create a blank Xamarin iOS app it will generate a Main.storyboard for the UI and this is hooked up in the Info.plist manifest so there will be no code in FinishedLaunching to setup the Window and root ViewController. The first step therefore is to un-set this:-

visible-touch-info

Then you can add code in the FinishedLaunching method to create the Window and load the storyboard.

public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
	// Set a custom Window which handles touch events
	Window = new InTheHand.TouchWindow(UIScreen.MainScreen.Bounds);

	// Load root view controller from Storyboard into Window.
	// You can't set this from Info.plist as it'll use a regular UIWindow.
	Window.RootViewController = UIStoryboard.FromName("Main", null).InstantiateInitialViewController();
	Window.MakeKeyAndVisible();

	return true;
}

Some solutions to this use graphics to display the touch circle but I’ve gone for straightforward UIKit drawing code with a circular UIBezierPath. The full code for a working demo app is on GitHub here:-

https://github.com/inthehand/VisibleTouch

You can see the effect of this code in this video:-

There are some limitations with this code. It supports single touch gestures only, if you use multiple fingers you’ll get a circle jumping around to the latest event. The circle can disappear quite abruptly, it will probably look better with a simple animation to fade out. I haven’t included code here to turn the visualisations on or off. Chances are you won’t want them on all the time so it would make sense to add a setting somewhere to toggle this. I did this myself using a settings bundle so you could toggle the setting before even running the app. That was interesting in itself and easier to integrate with Xamarin iOS than I expected but that is a topic for another blog post.

If there is significant interest in this I can release this as a NuGet package for easy integration into your Xamarin iOS project. For now you can take the TouchWindow.cs code and drop it into a project and modify the AppDelegate as described above.

Xamarin Forms MediaElement

Some time ago I created a MediaElement control for Xamarin Forms for displaying video (and audio) content across the main mobile platforms and it’s been steadily improving and has been used in a number of projects.

After some discussions on GitHub I started the process of integrating the control into Xamarin Forms itself with a Pull request opened last week. There will be some API changes in the transition and I’ll be sure to keep the current control available until its fully integrated into a stable Xamarin Forms release.

Things are progressing well with some reviews and code changes, partly to fit the Xamarin coding style and partly having fresh eyes looking at the code pointing out some improvements. I’ve already created a new renderer for WPF which supports the basics (The WPF MediaElement doesn’t have built-in transport controls so this isn’t supported yet). It highlighted something as I was testing on iOS where the video just wouldn’t work. I later remembered that unless you explicitly request it in your info.plist the app will block any http resources. But you’ll not get an error, the video will simply be stuck in the ReadyToPlay state. Switching to an https target fixed the issue immediately. This lead me to create the following table showing supported URI types and how they map on each platform. These apply to both the InTheHand.Forms version and the Xamarin.Forms control although only the latter has WPF support:-

 

Uri Scheme Android iOS UWP WPF
HTTP/HTTPS Yes HTTPS only or set NSAppTransportSecurity in info.plist Yes Yes
file Yes Yes Yes Yes
ms-appx Yes. Items must have the build action AndroidResource and be located in the Resources/raw folder of the project Yes. Items must have the build action BundleResource Yes Maps to pack://application:,,,{localpath}
ms-appdata Yes (Local+Temp) Yes (Local+Temp) Yes (Local, Temp and Roaming) Yes (Local+Temp)

More on Xamarin Insights Migration

Since the original release of InTheHand.AppCenter.Insights just over a week ago there have been a few hundred installs, which is nice. There has also been a major change at App Center and logging handled exceptions is now supported. Therefore the latest update (version 1.5) takes full advantage of this and utilises this new API so we don’t lose vital stacktrace information and these are correctly shown as errors rather than events with some of the exception properties. This is a great move from Microsoft as it brings the App Center functionality into parity with Xamarin Insights which has just three weeks remaining before it is shut down.

If you have a project currently using Xamarin Insights I recommend you install my library to quickly switch over before the deadline. If you’re already using my library I highly recommend you update to the latest version for the full exception reporting support.

From Xamarin Insights to Visual Studio AppCenter

If you haven’t already heard about Visual Studio AppCenter then you should check out this blog post introducing it. Microsoft have had multiple devops technologies which all lived in separate silos. Along with its own Azure Insights it inherited Xamarin Insights and Test Cloud when it acquired Xamarin and also HockeyApp. There was obviously a lot of overlap and a mammoth task of building a unified solution.

In terms of crash reporting and analytics AppCenter owes much to HockeyApp, there’s even built in handling of viewing HockeyApp data from AppCenter. No such luck from Xamarin Insights and you’ll need to find your own time to migrate across before Xamarin Insights is retired on March 31st. I had an idea that as a stop-gap solution it would be useful to have the same API as Xamarin Insights but move to the AppCenter back-end. Thus was born InTheHand.AppCenter.Insights. This is now available on NuGet and the rest of this post will look into switching over to it and a bit of information about finding the data in the AppCenter website.

The library exposes the same API as the original Xamarin.Insights package but AppCenter doesn’t support 100% of the functionality so there are some gotchas when using it. You shouldn’t need to modify any of your code and when you are ready you can start calling the AppCenter API directly and eventually phase out the wrapper.

To start, open your existing solution and remove the Xamarin.Insights NuGet package from your projects. Next add the InTheHand.AppCenter.Insights package. It is a .NET Standard 1.0 library for maximum compatibility, it could have been higher for the supported platforms but it doesn’t need any APIs beyond 1.0. Rebuilding your app now and it should all build without any errors against the new library.

I’ve used the Obsolete attribute on methods/properties which are not supported, calling them will just be a no-op but you can remove these calls if you don’t want the warnings.

Now we’re not quite finished – your code still uses an API key for Xamarin Insights. You’ll need to create a new App on AppCenter for the required platform(s) and this will give you a new key (a Guid string) and you’ll need to replace the old Xamarin API key with this one where you callĀ Insights.Initialize in each head project.

Once you’ve done this and rebuilt your app you’ll have working crash reporting and analytics going through to AppCenter and you haven’t had to re-write any code yet.

[Updated 8th March 2018] As of version 1.5 the Report method now supports logging exceptions to AppCenter as this functionality was recently added to AppCenter. You’ll see stack traces etc fully preserved in your App Center dashboard.

Remember I mentioned some gotchas earlier? Well there is one big gotcha – AppCenter doesn’t allow you to send handled exception reports – the Report method. So when the library processes these they are tracked analytics events with various properties of the Exception added. This means the presentation in AppCenter is not as good as the true crash reports. Unfortunately currently only the first 64 characters of the stack trace are preserved.

[Updated 28th February 2018] As of version 1.1 the library also supports Identify and will set these values using AppCenter’s SetCustomProperties feature. The unique ID you assign is given the name “Unique ID”.

AppCenter doesn’t natively support logging events with timing so the ITrackHandle implementation writes the total duration in seconds as part of the key/value data for the event.

A full crash entry looks like this in AppCenter:-

forcedCrash

An event generated by a reported exception looks like this:-

reportedException

The problem of course is that you don’t get reports logically grouped into exceptions thrown by the same code, instead you get a single “event” with the exception name and then properties which may indicate totally different stack traces. This is obviously not ideal but the danger of creating separate events using both the stack trace and exception is that there is a limit of 200 unique events. If you don’t manually delete ones you’ve finished working on as you might mark a crash as Closed you could hit this limit and lose data. I hope it is something that gets added to AppCenter soon, Microsoft promised this functionality would be in place before the Xamarin Insights shutdown but the deadline is fast approaching. When the API becomes available obviously I’ll update the wrapper as required. This is after all a temporary solution to help with the transition.

NuGet: InTheHand.AppCenter.Insights

 

ListView Adventures – Auto-sizing Uneven Rows

The Xamarin Forms ListView control has a tough job – it has to provide a platform agnostic, rich data-bindable control and yet take advantage of the performance and look-and-feel of the native control on each platform. I recently discovered an odd gotcha for a specific usage. It’s possible to have rows with different heights. There are a number of reasons you might want this, the simplest would be the case where you have multiple item templates to represent different types of item. A slightly more interesting scenario is a chat application. In this case you want each row to use the right amount of space for the message but you can’t hard-code specific row sizes. You need a template which you can measure and get an accurate height for that specific item obeying all the margins and spacing you’ve setup. As it turns out this doesn’t work on iOS and it is documented if you know where to look.

The solution is to add some extra logic and this can of course be done by writing a custom renderer. Since there is a performance overhead in building the list item and measuring each one you only want to do this in the case that you can’t hard-code a row height. To look at the out of the box behaviour see the first screenshot below. You can see some attempt has been made to resize the rows but they don’t actually fit the content correctly.

IMG_0102

The XAML for this view looks like this:-

<ListView ItemsSource="{Binding}" HasUnevenRows="True" BackgroundColor="LightGray" SeparatorVisibility="None">
 <ListView.ItemTemplate>
  <DataTemplate>
   <ith:AutoViewCell>
    <Frame Margin="20,10" HasShadow="True" CornerRadius="10">
     <Label Text="{Binding}"/>
    </Frame>
   </ith:AutoViewCell>
  </DataTemplate>
 </ListView.ItemTemplate>
</ListView>

As you can see I’ve define a new type derived from ViewCell. I’ve done this so that my renderer won’t be used for all ListView items but only those where we need this functionality. The AutoViewCellRenderer then does some extra work on iOS to set the item heights at runtime based on the data filled template. On Android and UWP it just uses the built in ViewCellRenderer which behaves as you’d expect.

[assembly:ExportRenderer(typeof(AutoViewCell), typeof(AutoViewCellRenderer))]
namespace InTheHand.Forms.Platform.iOS
{
 public sealed class AutoViewCellRenderer : ViewCellRenderer
 {
  public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
  {
   ViewCell vc = item as ViewCell;

   if (vc != null)
   {
    var sr = vc.View.Measure(tv.Frame.Width, double.PositiveInfinity, MeasureFlags.IncludeMargins);

    if (vc.Height != sr.Request.Height)
    {
     vc.ForceUpdateSize();

     sr = vc.View.Measure(tv.Frame.Width, double.PositiveInfinity, MeasureFlags.IncludeMargins);
     vc.Height = sr.Request.Height;
    }
   }

   return base.GetCell(item, reusableCell, tv);
  }
 }
}

It took a few goes to get this working correctly. First I checked if the vc.Height was -1, but found that this could be updated but still need re-measuring. Then I set upon the above which checks if the height matches the content and only if not calls ForceUpdateSize and measures again. This introduced a noticeable performance hit if called unnecessarily and this method could be called a lot when scrolling long lists. The result is the nicer looking:-

IMG_0103

This is part of InTheHand.Forms and will be rolled into the next NuGet release. Because the platform specific dll contains the renderer you need to call InTheHandForms.Init() to ensure it is registered.

Building a Better Button

I’ve used the Xamarin.Forms Button in a number of projects and while it has slowly improved (adding support for images etc) it’s still lacking in a few areas. Recently I needed to add support for wrapping and truncation options and these are mysteriously absent from the stock control.
For inspiration I looked at the Label control as this has a LineBreakMode property which allows you to use a variety of truncation or wrapping modes. This seemed perfect and rather than re-invent the wheel I set about adding the same property to the Button. Both iOS and Android support these options via LineBreakMode and Ellipsize properties respectively. Along the way I found it was necessary to improve the logic on iOS for sizing to fit content and the InTheHand.Forms.FormattedButton was born. I was also able to use this opportunity to fix a niggly issue with the iOS button where it fitted the label to the full width of the button so if you used a background the text was jammed against the left/right edges which looks ugly.
It’s intuitive to use from XAML:-

<cc:MyButton LineBreakMode="WordWrap" Text="This is a wrapping button with no other special properties set just a single long descriptive text value"/>

The new control is in the GitHub project and the NuGet package.