I was having a lot of trouble figuring out various aspects of Core Graphics, so I wrote this simple program to try out some things. Included in this are drawing some rectangles, a rounded rectangle, applying a gradient, rotated and centered text.
I used an UIImageView instead of deriving my own class directly from UIView because I wanted to see how that works. Note that introduces the interesting complexity of Cocoa graphics progamming by mixing the origin at top left (for making rectangles) and the bottom left (drawing into a bitmap graphics context).
This is truly a newbie effort, both for Quartz 2D programming and MonoTouch. Please make suggestions for improvements in the comments and I’ll republish another edition.
More details on building the main application and controller are available on the MonoTouch site, including sample programs and tutorials.
Update 10/11/09: Fixed code display.
Here’s the sample code:
[sourcecode language=”csharp”] /* Copyright (c) 2009, Terry Westley
Copying and distribution of this file, with or without modification, are permitted in any medium without royalty. This file is offered as-is, without any warranty. */
using System; using System.Drawing;
using MonoTouch.CoreGraphics; using MonoTouch.Foundation; using MonoTouch.UIKit;
namespace MT_SampleCoreGraphics { public class Application { static void Main (string[] args) { UIApplication.Main (args, null, “AppController”); } }
[Register (“AppController”)] public class AppController : UIApplicationDelegate { UIWindow window;
public override bool FinishedLaunching ( UIApplication app, NSDictionary options) { // Create the main view controller var vc = new MainViewController ();
// Create the main window and add main view // controller as a subview window = new UIWindow ( UIScreen.MainScreen.Bounds); window.AddSubview(vc.View);
window.MakeKeyAndVisible (); return true; }
// This method is allegedly required in iPhoneOS 3.0. // I don’t know what will happen if we leave it out. public override void OnActivated ( UIApplication application) { } }
[Register] public class MainViewController : UIViewController { private UIImageView imageView; private const float M_PI = (float)Math.PI;
public override void ViewDidLoad () { base.ViewDidLoad ();
float width = this.View.Frame.Width; float height = this.View.Frame.Height;
RectangleF wholeImageRect = new RectangleF ( 0, 0, width, height); imageView = new UIImageView (wholeImageRect); this.View.AddSubview (imageView);
CGBitmapContext context = new CGBitmapContext( System.IntPtr.Zero, // data (int)width, // width (int)height, // height 5, // bitsPerComponent 640, // bytesPerRow CGColorSpace.CreateDeviceRGB(), // colorSpace CGImageAlphaInfo.NoneSkipFirst);// bitmapInfo
// Start with filling the whole image with a // white background context.SetRGBFillColor (1, 1, 1, 1f); context.FillRect (wholeImageRect);
// Draw an opaque red rectangle // Inspired by “Quartz 2D Programming Guide,” // Listing 2-1 context.SetRGBFillColor (1, 0, 0, 1f); context.FillRect (new RectangleF (0, 0, 200, 100 ));
// Draw a partially transparent blue rectangle context.SetRGBFillColor (0, 0, 1, .5f); context.FillRect (new RectangleF (0, 0, 100, 200));
// Allow anti-aliasing context.SetAllowsAntialiasing(true);
// Gradient fill rounded rectangle. Inspired by: // http://www.iphonedevsdk.com/forum/iphone-sdk-development/5236-there-quartz-method-filling-rounded-rectangle-path-gradient.html RectangleF rect = new RectangleF( width/3, 200, width/3, height/3); float radius = 20; context.SaveState(); context.BeginPath(); context.MoveTo( (float)rect.Left + radius, (float)rect.Top); context.AddArc( (float)rect.Right - radius, (float)rect.Top + radius, radius, 3 * M_PI / 2, 0, false); context.AddArc( (float)rect.Right - radius, (float)rect.Bottom - radius, radius, 0, M_PI / 2, false); context.AddArc( (float)rect.Left + radius, (float)rect.Bottom - radius, radius, M_PI / 2, M_PI, false); context.AddArc( (float)rect.Left + radius, (float)rect.Top + radius, radius, M_PI, 3 * M_PI / 2, false); context.ClosePath(); context.Clip();
CGColorSpace colorSpace = CGColorSpace.CreateDeviceRGB(); CGGradient gradient = new CGGradient ( colorSpace, new float[] { 1, 0, 0, 1, // start color 0, 0, 1, 1 }, // end color new float[] { 0, 1 } ); // locations
context.DrawLinearGradient ( gradient, new PointF(rect.Left, rect.Top), // start point new PointF(rect.Right, rect.Bottom),// end point CGGradientDrawingOptions.DrawsAfterEndLocation);
// Restore clipping region to whole screen context.RestoreState();
// Display some text with 45 deg rotation // Inspired by “Quartz 2D Programming Guide,” // Listing 17-1 context.SelectFont (“Helvetica-Bold”, 50, CGTextEncoding.MacRoman); context.SetTextDrawingMode ( CGTextDrawingMode.FillStroke); context.SetRGBFillColor (0, 1, 0, 1); context.SetRGBStrokeColor (0, 0, 1, 1); context.TextMatrix = CGAffineTransform.MakeRotation(ToRadians(45)); ShowTextAtPoint (context, 30, 80, “Quartz 2D”);
// Not centered text int textHeight = 20; context.SelectFont (“Helvetica-Bold”, textHeight, CGTextEncoding.MacRoman); context.SetTextDrawingMode ( CGTextDrawingMode.Fill); context.SetRGBFillColor (0, 0, 0, 1); context.TextMatrix = CGAffineTransform.MakeRotation(ToRadians(0)); ShowTextAtPoint (context, width/2, height - textHeight, “Not Centered”);
// Centered text ShowCenteredTextAtPoint(context, width/2, height - 2*textHeight, “Centered”);
// Make an image out of the graphics context // and display it imageView.Image = UIImage.FromImage( context.ToImage());
Console.Write(“Switch to Simulator now to see “); Console.WriteLine(“some stupid graphics tricks”); }
private float ToRadians (float degrees) { return degrees * 0.01745329f; }
private void ShowTextAtPoint ( CGContext context, float x, float y, string text) { context.ShowTextAtPoint (x, y, text, text.Length); }
// Based on technique described in section // “Measuring Text Before Drawing” // of “Quartz 2D Programming Guide.” private void ShowCenteredTextAtPoint ( CGContext context, float centerX, float y, string text) { context.SetTextDrawingMode ( CGTextDrawingMode.Invisible); context.ShowTextAtPoint ( centerX, y, text, text.Length); context.SetTextDrawingMode ( CGTextDrawingMode.Fill); context.ShowTextAtPoint ( centerX - (context.TextPosition.X - centerX)/2, y, text, text.Length); } } } [/sourcecode]