MvvmCross WPF Slideout Menu Presenter

https://github.com/benhysell/V.SlideoutMenu

Sample MvvmCross WPF application with a slideout presenter. A slideout button is defined in the HomeView allowing one to switch between all three views. MenuOne has a button to show a full screen view, replacing the slideout view with a full screen view.

Usage

Applicaiton is made up of four Views:

  • Home - Main screen that holds the slideout button, slideout menu, and where menu items are shown
  • MenuOne - Sample View that also includes a button to show a full screen View
  • MenuTwo and MenuThree - Extra Views to fill out the application
  • DetailFullScreen - A full screen view that replaces the HomeView, creating a ‘modal’ screen. Button allows one to close this and return to the HomeView

SlideoutPresenter

SlideoutPresenter.cs in V.Slideout.Wpf\Utilitiles manages swapping and replacing Views based on user input. By decorating a View's xaml.cs class definition with a Region attribute will define where that View is shown.

i.e. From MenuOneView.xaml.cs

[Region(Region.BaseShowSlidingMenu)]
public partial class MenuOneView
{

Tells the SlideoutPresenter this View should show the slideout menu button and to replace the currently shown View in the HomeView.

For a full screen view DetailFullScreenView.xaml.cs is decorated with

[Region(Region.FullScreenNavigateBackwards)]
public partial class DetailFullScreenView
{

Telling the SlideoutPresenter to replace the entire HomeView with the contents of DetailFullScreenView. Once the user closes DetailFullScreenView the HomeView with the slideout button will again be shown the user. DetailFullScreenView is stacked on top of HomeView to take over the whole screen.

Slideout Menu

The slideout menu is in the HomeView grid that is hidden/shown via slideout menu button. A list of our items to show in the menu are created in the HomeViewModel and bound to a ListBox of buttons. Once a button is pressed the View is swapped out accordingly.

MvvmCross WPF Tabs Presenter

https://github.com/benhysell/V.Tabs

Sample MvvmCross WPF application with a tabs presenter. A tab bar is defined in the HomeView allowing one to switch between all three tabs. TabOne has a button to show a full screen view, replacing the tab view with a full screen view.

Usage

Application is made up of four Views:

  • Home - Main screen that holds the tab bar and where tabs are shown
  • TabOne - Sample tab that also includes a button to show a full screen view
  • TabTwo and TabThree - Extra tabs to fill out the application
  • DetailFullScreen - A full screen view that replaces the HomeView, creating a ‘modal’ screen. Button allows one to close this and return to the HomeView

TabPresenter

TabPresenter.cs in V.Tabs.Wpf\Utilitiles manages swapping and replacing Views based on user input. By decorating a View’s xaml.cs class definition with a Region attribute will define where that View is shown.

i.e. From TabOneView.xaml.cs

[Region(Region.Tab)]
public partial class TabOneView
{

Tells the TabPresenter this region is a tab and to replace the currently shown tab in the HomeView.

For a full screen View DetailFullScreenView.xaml.cs is decorated with

[Region(Region.FullScreen)]
public partial class DetailFullScreenView
{

Telling the TabPresenter to replace the entire HomeView with the contents of DetailFullScreenView. Once the user closes DetailFullScreenView the HomeView with the tabs will again be shown the user. DetailFullScreenView is stacked on top of HomeView to take over the whole screen.

MvvmCross Unified Xamarin.Android ViewPager and Xamarin.iOS UIPageControl/UIScrollView Example

This article provides a walkthrough for implementing a common MvvmCross.Core project for a Xamarin.Android and Xamarin.iOS applications allowing one to bind to Android’s ViewPager and use a UIPageControl/UIScrollView in iOS.

Motivation

In my app Goal Weight, I used a UIPageControl/UIScrollView combination to show the user three different graph views on the weight entry page.

graphs

Goal Weight was published before I discovered MvvmCross, so when I needed to implement a UIPageControl/UIScrollView in my latest MvvmCross application I did not have any source code to pull on.

My goal with my new application is to get the same functionality I had in Goal Weight using the MvvmCross framework.

Resources

Xamarin.Android Implementation

@Cheesebaron did all of the leg work creating a custom binding for the Android ViewPager. My goal with the iOS implementation was to take the ViewModels he created and make sure I can use them in iOS. I did not dive deep in to his Android binding implementation, I did test his sample application, and it worked as of the writing of this article.

Xamarin.iOS Implementation

Initially, I thought I was going to have to create a custom binding for the UIPageControl and UIScrollView controls, turns out I only needed to implement a custom view presenter.

ViewModels

We have two ViewModels in the sample application: * SimpleListViewModel - holds all of the pages we are about to create * SimpleViewModel - the page we are showing in the UIPageControl/UIScrollView

SimpleView

SimpleView is the page we are going to show, in this example it binds to the ViewModel's Name property, just to show that we have successfully bound to the ViewModel.

SimpleViewPagerView

SimpleViewPagerView holds the UIPageControl/UIScrollView and all of the logic to create the given number of SimpleViews defined in SimpleListViewModel.

All of the setup work takes place in ViewWillAppear. Below is a modified listing for brevity, see the GitHub repo for the full code listing.

public override void ViewWillAppear(bool animated)     
{
    base.ViewDidAppear(animated);   
    pageControl.ValueChanged += HandlePageControlValueChanged;
    scrollView = new UIScrollView()
    {
        ShowsHorizontalScrollIndicator = false,
        ShowsVerticalScrollIndicator = false,
        Bounces = true,
        PagingEnabled = true,
        Frame = UIScreen.MainScreen.Bounds
    };
    scrollView.Scrolled += HandleScrollViewDecelerationEnded;
    pageControl.Frame = new RectangleF(0, scrollView.Bounds.Bottom - 10, scrollView.Bounds.Width, 10);
    View.AddSubviews(scrollView, pageControl);

    int i;
    for (i = 0; i < ViewModel.Items.Count; i++)
    {
        var pageViewController = CreatePage(ViewModel.Items[i]);
        pageViewController.View.Frame = new RectangleF(UIScreen.MainScreen.Bounds.Width * i, 0, UIScreen.MainScreen.Bounds.Width, UIScreen.MainScreen.Bounds.Height);
        scrollView.AddSubview(pageViewController.View);
    }
    scrollView.ContentSize = new SizeF(UIScreen.MainScreen.Bounds.Width * (i == 0 ? 1 : i), UIScreen.MainScreen.Bounds.Height);              
    pageControl.Pages = i; 
}

First we create our UIPageControl and UIScrollView and then iterate through the SimpleListViewModel's Items which holds the pages we want to create, create those pages, and lastly add the created pages to the UIScrollView.

The trickiest part of ViewWillAppear is where we set he location of our page within the UIScrollView, we have to set the starting x position based on the number of the page we just created:

pageViewController.View.Frame = new RectangleF(UIScreen.MainScreen.Bounds.Width * i, 0, UIScreen.MainScreen.Bounds.Width, UIScreen.MainScreen.Bounds.Height);

Lastly we tell our UIPageControl how many pages we created and set the ContentSize of the UIScrollView accordingly:

scrollView.ContentSize = new SizeF(UIScreen.MainScreen.Bounds.Width * (i == 0 ? 1 : i), UIScreen.MainScreen.Bounds.Height);

We create the pages by using a MvvmCross built in function CreateViewControllerFor that takes a ViewModel and will create the View that goes along with it:

private UIViewController CreatePage(IMvxViewModel viewModel)
{
    var controller = new UINavigationController();           
    var screen = this.CreateViewControllerFor(viewModel) as UIViewController;              
    controller.PushViewController(screen, false);           
    return controller;
}

The only items left to do are to hook up the scroll events for the UIScrollView and UIPageControl, see the source code on GitHub for the full implementation.

Summary

Using @Cheesebaron’s custom Android bindings, and a custom presenter for iOS one can design a common MvvmCross core project that can feed both a Xamarin.Android and a Xamarin.iOS applications that use ViewPager and UIScrollView/UIPageControl respectively.

Use Asset Catalogs for a View’s Background Image in Xamarin.iOS

In my latest app Goal Length, I added the ability for the user to enable Touch ID / Passcode protection to launching the application. A user cannot open the application unless they authenticate via Touch ID or using their device’s Passcode.

For a smooth user experience while authenticating I wanted the application launch image to be displayed behind the Touch ID / Passcode dialog. From the user’s perspective the application would launch, my launch image would be shown while the application is launching, the application would finish launching, I’d load the launch image as the background of my authentication View, and then the Touch ID / Passcode dialog box would pop up asking the user authenticate.

IMG_4360

In this article I’ll cover how to successfully use Asset Catalogs for launch images and loading the proper background image for a given device. This will allow you to transition from a launch image to your first View while retaining the launch image in the background. Be advised, this technique works well most of the time, it fails when devices have the same resolution, but different physical dimensions, like the iPhone 5s and iPhone 6.

Resources

Why Asset Catalogs

Simply, asset catalogs make our lives easier. For this project I’ll be loading a different image based on the resolution of the device. Without an Asset Catalog I have to maintain a semi-complex if/else structure checking the device height for each of our possible devices. Before discovering Asset Catalogs my code looked something like this:

UIImage backgroundImage = null;

if (UIScreen.MainScreen.Bounds.Height == 480)
	backgroundImage = UIImage.FromFile (@"Icons/[email protected]");
else if (UIScreen.MainScreen.Bounds.Height == 568)
	backgroundImage = UIImage.FromFile (@"Icons/[email protected]");
else if (UIScreen.MainScreen.Bounds.Height == 667)
	backgroundImage = UIImage.FromFile (@"Icons/[email protected]");
else if (UIScreen.MainScreen.Bounds.Height == 736)
	backgroundImage = UIImage.FromFile (@"Icons/[email protected]");
else
//etc for each iPad resolution

View.BackgroundColor = UIColor.FromPatternImage(backgroundImage);

Note

In addition, and I’m not sure if this is a Xamarin bug or not, the files for each one of the devices needs to be named in the [email protected] format for each of the different resolutions. I had originally named the files their actual resolution names, i.e. [email protected] was named 640x970.png, but the scaling of the image in the background was completely wrong.

Even though we could solve our problem this way, we can do better.

Changing your Application to use Asset Catalogs

I’ll be working in Xamarin Studio for this article, as of this writing and Xamarin.iOS release this cannot be done in Visual Studio.

In Xamarin Studio first ensure your application is setup to use Asset Catalogs by right clicking on your project, select Options, from the Project Options select Build/iOS Application and scroll down to Universal Icons and Universal Launch Images and ensure Source is set properly for each.

Screen Shot 2014-11-18 at 10.26.19 AM

This will create a Resources folder in your application along with AppIcons.appiconset and LaunchImage.launchimage folders. Instead of adding your app icons and launch images in the project options we’ll now add them via the Resources/LaunchImage.launchimage/Contents.json file. Double click on Resources/LaunchImage.launchimage/Contents.jsonand let’s set our launch images.

Setting our Launch Images

First we are presented with a bunch of empty slots with resolution requirements for each device, and the iOS version the resolution corresponds to.

Screen Shot 2014-11-18 at 9.26.10 AM

In this example application I plan to support the following devices in portrait mode:

  • iPhone 4s
  • iPhone 5 / 5s
  • iPhone 6
  • iPhone 6 Plus
  • iPad 2 / iPad Mini
  • iPad Retina (iPad 3, iPad4, iPad Air, iPad Air 2)

So I’ll need to fill in each of the launch images for each of the devices.

Screen Shot 2014-11-18 at 9.27.01 AM

If we run our application we now have a launch image for each one of the devices.

Adding a Background Images Asset Catalog

Let’s now add our Background Images Asset Catalog. I can hear you saying, “Why not just use the Launch Image Asset Catalog as the source of our background images?” Believe me, I tried going down this path. Stay with me, you’ll see shortly why this does not work.

Right click on Resources/Images.xcassets and select Add/New Image Set.

Screen Shot 2014-11-18 at 10.35.35 AM

Rename the image set to BackgroundImages.imageset and double click on the Contents.json file. We’ll do the same procedure we did for the Launch Images asset catalog and fill in the following:

  • 2x iPhone
  • 3x iPhone
  • 1x iPad
  • 2x iPad

Screen Shot 2014-11-18 at 9.24.56 AM

We leave the R4 image empty, this would be the image that is set for the iPhone 5s / iPhone 6, it is shared by the iPhone 5s / iPhone 6.

As best as I can tell, since the overall resolutions for the iPhone 5s and iPhone 6 are identical, they are viewed as the same in the eyes of the Asset Catalog, if the user has an iPhone 5 or iPhone 6 the Asset Catalog will display the image in the R4 slot. This might be fine for most applications, but for us we are supplying an image that takes up the entirety of the screen, and we need two different images. Hence the Asset Catalog we just created cannot be leveraged by the iPhone 5s / iPhone 6 in this instance.

One would think the Launch Image Asset Catalog would take care of this problem for us, alas, no matter what I tried I could not make the Launch Image Asset Catalog select the proper image when running on the iPhone 5s or the iPhone 6.

Using Background Images Asset Catalog

Once we have everything configured in our project using the Background Images Asset Catalog using them in code is fairly straight forward. We’ll account for the fact we cannot load the proper images for the iPhone 5s / iPhone 6 from the Asset Catalog by checking the device’s Height. If the Height matches the iPhone 5s / iPhone 6 we’ll use a UIImage.FromFile call to load the proper image.

UIImage backgroundImage = null;
if (UIScreen.MainScreen.Bounds.Height == 568)
	backgroundImage = UIImage.FromFile(@"Icons/[email protected]");
else if (UIScreen.MainScreen.Bounds.Height == 667)
	backgroundImage = UIImage.FromFile(@"Icons/[email protected]");
else
	backgroundImage = UIImage.FromBundle("BackgroundImage");
View.BackgroundColor = UIColor.FromPatternImage(backgroundImage);

Remember, the iPhone 5s and iPhone 6 files need to be named [email protected] and [email protected] respectively, else they will not load properly.

Once implemented our user will launch the application, see the launch image from the Launch Image Asset Catalog, and then be shown the image from the Background Image Asset Catalog. Had I also implemented Authentication in Xamarin.iOS with Touch ID or Passcode, I could automatically present the user with the Touch ID / Passcode dialog box, thus creating a seamless transition into our application.

In the demo app you’ll know you are looking at the image from the Background Image Asset Catalog because I added an authentication button at the bottom of the screen.

iOS Simulator Screen Shot Nov 18, 2014, 7.20.00 PM

Summary

Asset Catalogs are nice additions to iOS development for managing and loading images in code. The only exception is when one needs a different image for devices that are physically different heights but have the same resolution, like the iPhone 5s / iPhone 6, we still need to manage loading those images by hand.

Working example source code can be found at https://github.com/benhysell/G.BackgroundImages, and my published application using these techniques can be found on the app store.

Authentication in Xamarin.iOS with Touch ID or Passcode

Adding Touch ID to your application is easy to do in iOS 8, in most cases this can be done in less than 20 lines of code. In this scenario the developer is responsible to supply a backup mechanism for the user if they choose not to use Touch ID for authentication. This type of Touch ID authentication is done using Local Authentication without Keychain Services.

Apple provides users the ability to use a Passcode to unlock their phone if they do not configure Touch ID, or choose not to use it, if say, all of their fingers are covered in jelly and they know their fingers will not successfully work with Touch ID, users can enter a Passcode to access their device.

What if we could use that same Passcode to protect our applications? With a few lines of code we can leverage Touch ID and use the user’s Passcode as a backup authentication mechanism.

Source Code

https://github.com/benhysell/V.TouchIdExample

Resources

Introduction to Touch ID / Using the Local Authentication Framework in your App

Xamarin provides an excellent introduction to Touch ID, Keychain Services, and Local Authentication on iOS. If you are new to the topic start here to familiarize yourself your options.

Adding Touch ID Authentication in iOS 8

Alex Blount provides the crib notes for adding Touch ID to your app via Local Authentication. From his example he shows how simple it is to get up and going, but this example lacks a backup mechanism if the user decides not to use Touch ID.

How to Use Apple‘s Touch ID Fingerprint API in Your Mobile App

Excellent article in obj-c on using Keychain Services, Touch ID, and the device’s Passcode as backup to Touch ID, and is the basis of my approach in this article.

Xamarin.iOS Keychain Example

Xamarin provides the insight on how to work with Apple’s Keychain Services which we’ll leverage to launch Touch ID / Passcode authentication.

Motivation

In my latest application, Goal Length, I designed an application that provides users an easy method to record different body measurements on a daily basis. Measure your neck, chest, waist, ect, record them, and see from day to day, week to week, if you are physically shrinking or growing, the tape measure doesn’t lie. The scale can report you are putting weight on, but you could be loosing inches on your waist. Without body measurements you might think your not progressing towards your goals.

Information like this isn’t something someone typically wants easily accessible. Hand your unprotected phone to your friend and they can be navigating through your last several body measurements, which could prove embarrassing.

Most Touch ID implementations besides the Apple Home Screen Lock, are meant for the user to supply a user name and password to protect a service, say a log in to a website. Hence, typical implementations of Touch ID protection rely on the developer to provide the backup mechanism for the user to enter their password specific to that service.

Since I’m just looking to protect the application from prying eyes, and I don’t have a application specific login/password I wanted to use the user’s already configured Touch ID / Passcode. Why make someone memorize one more password?

Implementation

Note

The demo code for Touch ID / Passcode supplied in this article will not run properly on the simulators. Since you cannot set a Passcode on the simulators you’ll need to run and test your code on a real device to see it work.

The demo also assumes you have Touch ID / Passcode enabled on your device. If you do not have Touch ID enabled but you do have a Passcode configured the application with skip to the Passcode entry screen.

Architecture

The sample application allows you to play with turning Touch ID / Passcode protection for an application on and off, and authenticating with each of the methods.

One can : * Turn Touch ID on and off. Turning Touch ID off requires the user to authenticate one last time before allowing them to turn it off. IMG_4355 * Once Touch ID is enabled the user can authenticate via Touch ID IMG_4357 * If the user decides to not use Touch ID they can use their device’s Passcode to authenticate IMG_4358 * If successful ‘Authenticated!’ is shown to the user. IMG_4359

Code

From the How to Use Apple‘s Touch ID Fingerprint API in Your Mobile App, we learn if we want to use Passcode as a fallback mechanism we need to interact with Apple’s Keychain. Basically, we are going to store some dummy information in Apple’s Keychain and then retrieve it via Touch ID / Passcode. It doesn’t matter what we store in the Keychain, we are not going to use it, but we are looking to trigger the workflow of presenting the user with the Touch ID / Passcode prompts, and be notified if our user was successful authenticating or not.

Turning on Touch ID

Let’s create our fake Keychain record.

//set our record
//note what you fill in here doesn't matter, just needs to be consistent across all uses of the record
var secRecord = new SecRecord(SecKind.GenericPassword)
{
	  Label = "Keychain Item",
      Description = "fake item for keychain access",
      Account = "Account",
      Service = "com.goallineapps.touchIdExample",
      Comment = "Your comment here",
      ValueData = NSData.FromString("my-secret-password"),
      Generic = NSData.FromString("foo")
};

secRecord.AccessControl = new SecAccessControl(SecAccessible.WhenPasscodeSetThisDeviceOnly, SecAccessControlCreateFlags.UserPresence);
SecKeyChain.Add(secRecord);
				                
authenticateButton.Enabled = true;

The AccessControl flags of SecAccessible.WhenPasscodeSetThisDeviceOnly and SecAccessControlCreateFlags.UserPresence only allow us to store this record if the device has a passcode, and then retrieve it if the user is present.

Accessing our Keychain Record

Now that we have saved away our Keychain record, let’s try to access it.

partial void AuthenticateUser(UIButton sender)
{
	var rec = new SecRecord(SecKind.GenericPassword)
 	{
		Service = "com.goallineapps.touchIdExample",
		UseOperationPrompt = "Authenticate to access Test App"
	};
	SecStatusCode res;
	SecKeyChain.QueryAsRecord(rec, out res);
 	if (SecStatusCode.Success == res || SecStatusCode.ItemNotFound == res)
 	{
		//Success!!  
		//add your code here to continue into your application
 		AuthenticatedLabel.Hidden = false;
 	}
 	else
 	{
		//Failure
 		AuthenticatedLabel.Hidden = true;
 	}
 }

Pressing our ‘Authenticate’ button will kick off an attempt to access our dummy Keychain item. Since we already told the system the item is protected with the flags SecAccessible.WhenPasscodeSetThisDeviceOnly and SecAccessControlCreateFlags.UserPresence this kicks off the Touch ID / Passcode workflow.

Turning Off Touch ID

For completeness, I force the user to authenticate one last time before allowing them to turn off Touch ID authentication. Once authenticated it will delete the record from the device.

//disable Touch ID
var record = new SecRecord(SecKind.GenericPassword)
{
	Service = "com.goallineapps.touchIdExample",
	UseOperationPrompt = "Authenticate to Remove Touch ID / Passcode from Test App"
};

SecStatusCode result;

//query one last time to ensure they can remove it
SecKeyChain.QueryAsRecord(record, out result);
if (SecStatusCode.Success == result || SecStatusCode.ItemNotFound == result)
{
	//remove the record
	SecKeyChain.Remove(record);
	authenticateButton.Enabled = false;
}
else
{
	//could not authenticate, leave switch on
	sender.On = true;
}  

Summary

If you are looking to leverage the user’s existing Passcode to protect your application you can store a dummy value in the Keychain Service and then attempt to access it within your application to force an authentication attempt by the user via either Touch ID or Passcode.