MonoTouch Sample: Core Graphics and UIImageView

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:

/*
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);
        }
    }
}
Advertisements
This entry was posted in iPhone Dev and tagged , , , , . Bookmark the permalink.

7 Responses to MonoTouch Sample: Core Graphics and UIImageView

  1. Pingback: MonoTouch.Info

  2. toth3max says:

    Thank you! Nice sample! Found it trough the MonoTouch mailing list.

  3. Say, reading through the FAQ, http://monotouch.net/FAQ, it seems to say that Mono Touch doesn’t run SL/XAP apps. And SL, at least, would require a browser running on the iPhone that would allow the Silverlight plug-in.

    So is the idea that you take whatever SL/WPF APIs that they do expose, and write new C# code to sorta kinda create an iPhone “look and feel” that resembles what original SL/WPF app?

  4. Christian Weyer says:

    Ah, works great. Thanks!
    But… I have text with German umlauts and they are not displayed correctly. Any idea how to solve this?

  5. Sebastian says:

    Terry,

    Have you tried actually creating a signature type rect in which you could sign then save as a byte[] array?

    I wanted to find out if you could take a look at one of my methods…

  6. twestley says:

    What is a “signature type rect?” I’m not familiar with that phrase.

  7. Pingback: Creating an animated spinner in a MonoTouch UIImageView - PatridgeDev

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s