Skip to main content

Custom PageControl

By July 24, 2012December 18th, 2013Blog

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!