Custom PageControl

By July 24, 2012Blog

The standard UIPageControl does not allow custom sizes and colors for the page indicators. This is an implementation that fixes that.

 

using System;
using System.Drawing;
 
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.CoreGraphics;
 
namespace MonoTouch.UIKit.Extensions
{
	public enum PageControlType
	{
		OnFullOffFull,
		OnFullOffEmpty,
		OnEmptyOffFull,
		OnEmptyOffEmpty
	}
 
	public class PageControl : UIControl
	{
		private int numberOfPages;
		public int Pages 
		{ 
			get
			{
				return numberOfPages;
			}
 
			set
			{
				// make sure the number of pages is positive
				numberOfPages = Math.Max(0, value);
 
				// we then need to update the current page
				currentPage = Math.Min(Math.Max(0, currentPage), numberOfPages - 1);
 
				// correct the bounds accordingly
				this.Bounds = this.Bounds;
 
				// we need to redraw
				this.SetNeedsDisplay ();
 
				// depending on the user preferences, we hide the page control with a single element
				if (HidesForSinglePage && (numberOfPages < 2))
					this.Hidden = true;
				else
					this.Hidden = false;
			}
		}
 
		private int currentPage;
		public int CurrentPage 
		{ 
			get
			{
				return currentPage;
			}
 
			set
			{
				// no need to update in that case
				if (currentPage == value)
					return;
 
				// determine if the page number is in the available range
				currentPage = Math.Min(Math.Max(0, value), numberOfPages - 1);
 
				this.SetNeedsDisplay ();
			}
		}
 
		private bool hidesForSinglePage;
		public bool HidesForSinglePage 
		{ 
			get
			{
				return hidesForSinglePage;
			}
 
			set
			{
				hidesForSinglePage = value;
 
				// depending on the user preferences, we hide the page control with a single element
				if (hidesForSinglePage && (numberOfPages < 2))
					this.Hidden = true;
			}
		}
 
 
		public bool DefersCurrentPageDisplay { get; set; }
		public PageControlType ControlType  { get; set; }
		public UIColor OnColor { get; set; }
		public UIColor OffColor { get; set; }
 
		public float IndicatorDiameter { get; set; }
		public float IndicatorSpace { get; set; }
 
		private const float kDotDiameter = 4.0f;
		private const float kDotSpace = 12.0f;
 
		/// <summary>
		/// Initializes a new instance of the <see cref="ieMobile.PageControl"/> class.
		/// </summary>
		/// <param name='ct'>
		/// Ct.
		/// </param>
		/// <param name='frame'>
		/// Frame.
		/// </param>
		public PageControl (PageControlType ct, RectangleF rect) : base (rect)
		{
			ControlType = ct;
			DefersCurrentPageDisplay = false;
 
			this.BackgroundColor = UIColor.Clear;			
		}
 
		/// <summary>
		/// Sizes for number of pages.
		/// </summary>
		/// <returns>
		/// The for number of pages.
		/// </returns>
		/// <param name='pageCount'>
		/// Page count.
		/// </param>
		public SizeF SizeForNumberOfPages (int pageCount)
		{
			float diameter = (IndicatorDiameter > 0) ? IndicatorDiameter : kDotDiameter;
			float space = (IndicatorSpace > 0) ? IndicatorSpace : kDotSpace;
 
			return new SizeF(pageCount * diameter + (pageCount - 1) * space + 44.0f, Math.Max(44.0f, diameter + 4.0f));
		}
 
		/// <summary>
		/// Draws the rect.
		/// </summary>
		/// <param name='area'>
		/// Area.
		/// </param>
		/// <param name='formatter'>
		/// Formatter.
		/// </param>
		public override void Draw (RectangleF area)
		{
			base.Draw (area);
 
			// get the current context
			CGContext context = UIGraphics.GetCurrentContext ();
 
			// save the context
			context.SaveState ();
 
			// allow antialiasing
			context.SetAllowsAntialiasing (true);
 
			// get the caller's diameter if it has been set or use the default one 
			float diameter = (IndicatorDiameter > 0) ? IndicatorDiameter : kDotDiameter;
			float space = (IndicatorSpace > 0) ? IndicatorSpace : kDotSpace;
 
			// geometry
			RectangleF currentBounds = this.Bounds;
			float dotsWidth = this.Pages * diameter + Math.Max(0, this.Pages - 1) * space;
			float x = currentBounds.GetMidX () - dotsWidth / 2;
			float y = currentBounds.GetMidY () - diameter / 2;
 
			// get the caller's colors it they have been set or use the defaults
			CGColor onColorCG = OnColor != null ? OnColor.CGColor : UIColor.FromWhiteAlpha(1.0f, 1.0f).CGColor;
			CGColor offColorCG = OffColor != null ? OffColor.CGColor : UIColor.FromWhiteAlpha(0.7f, 0.5f).CGColor;
 
			// actually draw the dots
			for (int i = 0; i < Pages; i++)
			{
				RectangleF dotRect = new RectangleF (x, y, diameter, diameter);
 
				if (i == CurrentPage)
				{
					if (ControlType == PageControlType.OnFullOffFull || ControlType == PageControlType.OnFullOffEmpty)
					{
						context.SetFillColorWithColor (onColorCG);
						context.FillEllipseInRect(dotRect.Inset (-1.0f, -1.0f));
					}
					else
					{
						context.SetStrokeColorWithColor (onColorCG);
						context.StrokeEllipseInRect (dotRect);
					}
				}
				else
				{
					if (ControlType == PageControlType.OnEmptyOffEmpty || ControlType == PageControlType.OnFullOffEmpty)
					{
						context.SetStrokeColorWithColor (offColorCG);
						context.StrokeEllipseInRect (dotRect);
					}
					else
					{
						context.SetFillColorWithColor (offColorCG);
						context.FillEllipseInRect (dotRect.Inset (-1.0f, -1.0f));
					}
				}
 
				x += diameter + space;
			}
 
			// restore the context
			context.RestoreState ();
		}
 
		/// <summary>
		/// Toucheses the ended.
		/// </summary>
		/// <param name='touches'>
		/// Touches.
		/// </param>
		/// <param name='evt'>
		/// Evt.
		/// </param>
		public override void TouchesEnded (NSSet touches, UIEvent evt)
		{
			base.TouchesEnded (touches, evt);
 
			// get the touch location
			UITouch theTouch = touches.AnyObject as UITouch;
			PointF touchLocation = theTouch.LocationInView (this);
 
			// check whether the touch is in the right or left hand-side of the control
			if (touchLocation.X < (this.Bounds.Size.Width / 2))
				this.CurrentPage = Math.Max(this.CurrentPage - 1, 0) ;
			else
				this.CurrentPage = Math.Min(this.CurrentPage + 1, Pages - 1) ;
 
			// send the value changed action to the target
			this.SendActionForControlEvents (UIControlEvent.ValueChanged);
		}
	}
}

 

Usage is pretty simple.

 

var pageControl = new PageControl (PageControlType.OnFullOffFull, rectanglePosition);
pageControl.OnColor = UIColor.Blue;
pageControl.OffColor = UIColor.DarkGray;
pageControl.Pages = num of pages;

 

Happy coding!