MvvmCross Unified Xamarin.Android ViewPager and Xamarin.iOS UIPageControl/UIScrollView Example
23 Nov 2014This 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.
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
- https://github.com/benhysell/Cheesebaron.MvvmCross.Bindings - Source code for this article
- https://github.com/Cheesebaron/Cheesebaron.MvvmCross.Bindings - Android implementation and inspiration for my approach. @Cheesebaron is a prolific MvvmCross contributor, if you are using the MvvmCross framework I highly suggest you follow him on Twitter, check out his GitHub, and subscribe to his blog.
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.