Category

Blog

Flipboard flippin’ control

By | Blog

So as usual, once there is something original, everyone wants that in their app as well. And with Flipboard and their new paradigm in user experience and browsing, it is no wonder that there is plenty of discussion to have this in Monotouch as well.

So here we go, welcome FlipView. Source code at the GitHub.

http://github.com/sichy/flipview

Enjoy and happy coding!

Get UIImage from captured CMSampleBuffer

By | Blog

When working with the video capture, the image buffer is not usually enough and we need to pick up the UIImage from the buffer.

UIImage ImageFromSampleBuffer (CMSampleBuffer sampleBuffer)
			{
				// Get the CoreVideo image
				using (var pixelBuffer = sampleBuffer.GetImageBuffer () as CVPixelBuffer){
					// Lock the base address
					pixelBuffer.Lock (0);
					// Get the number of bytes per row for the pixel buffer
					var baseAddress = pixelBuffer.BaseAddress;
					int bytesPerRow = pixelBuffer.BytesPerRow;
					int width = pixelBuffer.Width;
					int height = pixelBuffer.Height;
					var flags = CGBitmapFlags.PremultipliedFirst | CGBitmapFlags.ByteOrder32Little;
					// Create a CGImage on the RGB colorspace from the configured parameter above
					using (var cs = CGColorSpace.CreateDeviceRGB ())
					using (var context = new CGBitmapContext (baseAddress,width, height, 8, bytesPerRow, cs, (CGImageAlphaInfo) flags))
					using (var cgImage = context.ToImage ()){
						pixelBuffer.Unlock (0);
						return UIImage.FromImage (cgImage);
					}
				}
			}

 

Convert time in UIDatePicker to local

By | Blog

When working with UIDatePicker time mode, it is typical that the time is picked in UTC and it needs to be converted to the local time and DateTime representation for purposes of showing it, setting up an alarm and such.

 

NSDate sourceDate = picker.Date;
 
NSTimeZone sourceTimeZone = new NSTimeZone ("UTC");
NSTimeZone destinationTimeZone = NSTimeZone.LocalTimeZone;
 
int sourceGMTOffset = sourceTimeZone.SecondsFromGMT (sourceDate);
int destinationGMTOffset = destinationTimeZone.SecondsFromGMT (sourceDate);
int interval = destinationGMTOffset - sourceGMTOffset;
 
var destinationDate = sourceDate.AddSeconds (interval);
 
var dateTime = new DateTime(2001, 1, 1, 0, 0, 0).AddSeconds(destinationDate.SecondsSinceReferenceDate);

 

Happy coding!

 

Custom PageControl

By | Blog

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!

 

 

PerformSelector extension

By | Blog

While back I wrote an post about perform selector helper with pure delegates and a helper class. I then changed it a bit to use NSAction for easier use. So here it is:

 

namespace MonoTouch.Foundation.Extensions
{
	public static class CoreFoundationExtensions
	{
		public static void PerformSelector (this NSObject obj, NSAction action, float delay)
		{
			int d = (int)(1000 * delay);
 
			var thread = new Thread(new ThreadStart ( () => {
				using(var pool = new NSAutoreleasePool())
    				{
						Thread.Sleep (d); 
						action.Invoke ();
    				}
			 }));			
 
			thread.IsBackground = true;
			thread.Start();
		}
 
		public static void PerformSelector (this NSObject obj, NSAction action)
		{
			PerformSelector (obj, action, 0.001f);
		}
	}
}

 

Happy coding!

 

UIDickBar in MonoTouch

By | Blog

New Twitter client for iPhone has a specific UI element that is utterly disruptive and annoying. Should you wish to annoy users of you application written in Monotouch, please feel free to use this port of original UIDickBar from Git hub: UIDickBar

And the most important part, download the Monotouch port here.

Control itself is coded as subclass of UIControl with custom drawing using Draw override and CGContext and so on, so a nice sample of how to use such approach.

To use it in you code simply call:

 

dickBar = new touch4apps.UIDickBar.Controls.UIDickBar ("Dick title", "Dick badge");
dickBar.DickBarAction += HandleDickBarDickBarAction;
dickBar.ShowInView (this.View);

 

Enjoy and happy coding!

UITapGestureRecognizer and UIBarButtonItem

By | Blog

When you have problems with UIBarButtonItem receiving tap events when the main View has UITapGestureRecognizer, it might be because you have UIToolbar and the actual UITapGestureRecognizer added to the same View. Then the recognizer receives all the tap events even those on the UIBarButtonItems.

Ensure you handle the ShouldReceiveTouch accordingly and do not return true if the tap is on the UIBarButtonItem. This is how I handle it:

 

public class TapGestureRecognizerDelegate : UIGestureRecognizerDelegate
{
	public UITouch ActiveTouch { get; set; }
 
	public override bool ShouldReceiveTouch (UIGestureRecognizer recognizer, UITouch touch)
	{
		if (touch.View.Superview is UIToolbar)
			return false;
 
		ActiveTouch = touch;
 
		return true;
	}
}

 

Happy coding!

 

UIImage retina safe scaling & drawing

By | Blog

When working on our next app, we found us in a situation where we have lots of graphics in some size and need to present it on the screen in several scaled versions.

The issue is, that graphics can be prepared for each of the sizes needed, but then, it will be redundant. Or find a way how to scale it in the code for the next drawing part.

And of course, there is a coding solution (how else? :-)). The trick is to draw the image to the context created for the screen, so it is properly sized, nice feature out of the box in iOS is that it will do the retina fine tuning automatically. We just need to draw it.

 

public static class UIImageHelper
{
	public static UIImage FromFile (string filename, SizeF fitSize)
	{
		var imageFile = UIImage.FromFile (filename);
 
		return imageFile.ImageToFitSize(fitSize);
	}
 
	public static UIImage ImageToFitSize (this UIImage image, SizeF fitSize)
	{
  	    double imageScaleFactor = 1.0;
	    imageScaleFactor = image.CurrentScale;
 
	    double sourceWidth = image.Size.Width * imageScaleFactor;
	    double sourceHeight = image.Size.Height * imageScaleFactor;
	    double targetWidth = fitSize.Width;
	    double targetHeight = fitSize.Height;
 
	    double sourceRatio = sourceWidth / sourceHeight;
	    double targetRatio = targetWidth / targetHeight;
 
	    bool scaleWidth = (sourceRatio <= targetRatio);
	    scaleWidth = !scaleWidth;
 
	    double scalingFactor, scaledWidth, scaledHeight;
 
	    if (scaleWidth) 
	    {
	        scalingFactor = 1.0 / sourceRatio;
	        scaledWidth = targetWidth;
	        scaledHeight = Math.Round (targetWidth * scalingFactor);
	    } 
	else 
	{
	        scalingFactor = sourceRatio;
	        scaledWidth = Math.Round(targetHeight * scalingFactor);
		scaledHeight = targetHeight;
	}
 
		RectangleF destRect = new RectangleF(0, 0, (float)scaledWidth, (float)scaledHeight);
 
		UIGraphics.BeginImageContextWithOptions(destRect.Size, false, 0.0f);
		image.Draw (destRect); 
		UIImage newImage = UIGraphics.GetImageFromCurrentImageContext ();
		UIGraphics.EndImageContext ();
 
		return newImage;
	}
}

 

Happy coding!

 

Drawing erase helper

By | Blog

For one of our app we needed drawing technique that would delete the image in graphics context based on the user input and act like a erase rubber.

Of course there is again everything needed in MonoTouch to do the job.


First we need to get the drawing context of an view object (UIImageView, but can be anything else) and prepare it for the painting. As the CGContext is LL (lower left) coordinates and not UL (upper left) coordinate system, there is a little trick needed to translate the coordinates.

 

public void EraseStart ()
{
	if (drawingBoard == null)
		return;
 
	UIGraphics.BeginImageContext (drawingBoard.Size);
 
	// erase lines
	ctx = UIGraphics.GetCurrentContext ();
 
	// Convert co-ordinate system to Cocoa's (origin in UL, not LL)
	ctx.TranslateCTM (0, drawingBoard.Size.Height);
	ctx.ConcatCTM (CGAffineTransform.MakeScale (1, -1));
 
	ctx.DrawImage (new RectangleF (0, 0, drawingBoard.Size.Width, drawingBoard.Size.Height),
             drawingBoard.CGImage);
}

 

Okay, that was not that bad, so now to the even easier part, to erase the content from the context and put it back to the view so it disappears from the view. To do that we will simply put clip to the context based on the point and we will add it as arc object.

 

public void ContextErasePoint (PointF point, int thickness)
{
	if (drawingBoard == null)
		return;
 
	RectangleF circleRect = new RectangleF (point.X - thickness / 2, 
		drawingBoard.Size.Height - point.Y - thickness / 2, thickness, thickness);
 
	ctx.AddArc (point.X, drawingBoard.Size.Height - point.Y, thickness / 2, 0f, 2f * (float)Math.PI, false);
	ctx.Clip();
 
	ctx.ClearRect (circleRect);
}

 

And now just put the image back from context to the image view so user can see it.

 

public void EraseEnd ()
{
	drawingBoard = UIGraphics.GetImageFromCurrentImageContext ();
	UIGraphics.EndImageContext ();
 
	drawingView.Image = drawingBoard;
}
 

 

That’s it.

Happy coding!

 


 

P.S. There is a sample project to download, with all the goodies working.
P.P.S. If you like my posts, please share the love and tell your friends and support me and buy my apps. Check touch4apps.com for more info. Thank you.

And here is the Github link to the sample code.
And sample video of it in action  

 

 

MonoTouch UITableViewController transparent background

By | Blog

Just came across a problem to have UITableViewController on a custom texture, wanted and needed to have the nice rounded corners as well and found out it is not that straightforward to set the background color to transparent in case that running on iPad (3.2 and 4.2 as well).

So here is a little code snippet that does the trick.

 

// create the UITableViewController
SettingTableViewController tableController = new SettingTableViewController(UITableViewStyle.Grouped);
 
// set its color to transparent as well, as this is needed, this is the first layer of the setup
tableController.View.BackgroundColor = UIColor.Clear;
tableController.View.Frame = new RectangleF(12, 101, 280, 98);
 
// create a dummy view for the underlaying view			
UIView view = new UIView();
view.Frame = tableController.View.Frame;
 
// make its color transparent as well
view.BackgroundColor = UIColor.Clear;
 
// and now the little trick, get to the core of the view hierarchy and set the view from the original UIImageView, in which the UITableView of the controller lays, to our custom transparent view
((UITableView)tableController.View).BackgroundView = view;
 
 

 

Happy coding!