IT tutorials
 
Mobile
 

iPad Development : The Dual-Action Color Popover (part 1) - Creating a Simple Color Grid

12/3/2011 4:28:17 PM
- How To Install Windows Server 2012 On VirtualBox
- How To Bypass Torrent Connection Blocking By Your ISP
- How To Install Actual Facebook App On Kindle Fire
We're down to just two popovers left to implement, and they're both actually the same. What we need is a simple color picker that lets the user set colors for either stroke or fill, depending on which button is clicked.

NOTE

We wouldn't need to implement a color selector popover if iOS included some sort of color picker (along the lines of Mac OS X's NSColorPanel, for instance), but it currently does not.

Recall that our implementation of DudelViewController works by checking each dismissed popover by class to see which one it was. So, we'll implement the color selector GUI in one class, but use two subclasses to create the popovers, so that we can tell which is which when it's dismissed.

To keep things simple, we're just going to let the user pick from a simple grid that shows 12 colors, as shown in. An additional view at the top of the GUI will show the currently selected color.

Figure 1. Our simple color picker (I know you're probably seeing this in black and white, so please take my word for it when I tell you that those are colors.)

As for the user interaction, it seems natural that this popover should have a "touch-and-dismiss" policy, unlike the stroke width and font size popovers, which hang around to let users move the slider multiple times until they got it just right. For the color selector, we'll let the users drag their finger around the grid, always displaying the latest color in the view at the top, and dismiss the popover as soon as they release their finger.

1. Creating a Simple Color Grid

Let's start by making a view class that just knows how to display a grid of colors, and respond to touch events by sending notifications containing the touched color. Later, our view controller class will register as an observer for those notifications. Create a new UIView subclass called ColorGrid, and put the following code in ColorGrid.h:

//  ColorGrid.h
#import <UIKit/UIKit.h>
// notification names
#define ColorGridTouchedOrDragged @"ColorGridTouchedOrDragged"
#define ColorGridTouchEnded @"ColorGridTouchEnded"
// key into the notification's userInfo dictionary
#define ColorGridLatestTouchedColor @"ColorGridLatestTouchedColor"
@interface ColorGrid : UIView {
NSArray *colors;
NSUInteger columnCount;
NSUInteger rowCount;
}
@property (retain, nonatomic) NSArray *colors;
@property (nonatomic) NSUInteger columnCount;
@property (nonatomic) NSUInteger rowCount;
@end

This interface shows all the elements we'll need in order to use this class: a set of properties for specifying the colors, as well as the number of columns and rows to display, all of which need to be set in order for the view to draw properly. Here, we also define a pair of NSString constants that interested parties (such as our controller class) will use to register themselves as NSNotification observers, and another string that's used as a key into the userInfo dictionary passed along with the notification for retrieving the chosen color. It's a good idea to define strings that will be used in multiple spots this way, instead of putting the literal strings, quotes and all, in your code. With the defined version, Xcode will help autocomplete as you type, and the compiler will complain if you misspell it.

Now for the implementation. Switch to ColorGrid.m, and start things off with the basics:

//  ColorGrid.m
#import "ColorGrid.h"
@implementation ColorGrid
@synthesize colors, columnCount, rowCount;
- (void)dealloc {
self.colors = nil;
[super dealloc];
}

Next up is the drawRect: method. This method relies on columnCount and rowCount being set to a nonzero value before being drawn. Those values determine the layout of the grid as a whole. The UIColor objects stored in the colors array will be used to fill rectangles in the grid, row by row. If there aren't enough colors in the array to fill the grid, the rest of the "cells" will be filled with white.

- (void)drawRect:(CGRect)rect {
CGRect b = self.bounds;

CGContextRef myContext = UIGraphicsGetCurrentContext();
CGFloat columnWidth = b.size.width / columnCount;
CGFloat rowHeight = b.size.height / rowCount;
for (NSUInteger rowIndex = 0; rowIndex < rowCount; rowIndex++) {
for (NSUInteger columnIndex = 0; columnIndex < columnCount; columnIndex++) {
NSUInteger colorIndex = rowIndex * columnCount + columnIndex;
UIColor *color = [self.colors count] > colorIndex ?
[self.colors objectAtIndex:colorIndex] :
[UIColor whiteColor];
CGRect r = CGRectMake(b.origin.x + columnIndex * columnWidth,
b.origin.y + rowIndex * rowHeight,
columnWidth, rowHeight);
CGContextSetFillColorWithColor(myContext, color.CGColor);
CGContextFillRect(myContext, r);
}
}
}


We also need to be able to determine the color shown at any given point, for the touch methods to be able to report with a notification. Rather than putting that directly into the touch methods, we split it off into a separate colorAtPoint: method that each of them can use. This is basically the inverse of what's going on in the innermost loop of the drawRect: method.

- (UIColor *)colorAtPoint:(CGPoint)point {
if (!CGRectContainsPoint(self.bounds, point)) return nil;

CGRect b = self.bounds;
CGFloat columnWidth = b.size.width / columnCount;
CGFloat rowHeight = b.size.height / rowCount;
NSUInteger rowIndex = point.y / rowHeight;
NSUInteger columnIndex = point.x / columnWidth;
NSUInteger colorIndex = rowIndex * columnCount + columnIndex;
return [self.colors count] > colorIndex ?
[self.colors objectAtIndex:colorIndex] :
nil;
}

Finally, we get to the touch methods themselves. This class responds to both initial touches and drags in the same way, so touchesMoved: just calls touchesBegan:. However, touchesEnded: uses a different notification name, so we'll let it have its own code.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint location = [[touches anyObject] locationInView:self];
UIColor *color = [self colorAtPoint:location];
if (color) {
NSDictionary *userDict = [NSDictionary dictionaryWithObject:color
forKey:ColorGridLatestTouchedColor];
[[NSNotificationCenter defaultCenter] postNotificationName:ColorGridTouchedOrDragged
object:self userInfo:userDict];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

CGPoint location = [[touches anyObject] locationInView:self];
UIColor *color = [self colorAtPoint:location];
if (color) {
NSDictionary *userDict = [NSDictionary dictionaryWithObject:color
forKey:ColorGridLatestTouchedColor];
[[NSNotificationCenter defaultCenter] postNotificationName:ColorGridTouchEnded
object:self userInfo:userDict];
}
}
@end

 
Others
 
- XNA Game Studio 3.0 : Creating Fake 3-D - Creating Shadows Using Transparent Colors
- Android Application Development : ViewGroups (part 2) - ListView, ListActivity, ScrollView & TabHost
- Android Application Development : ViewGroups (part 1) - Gallery and GridView
- Java ME on Symbian OS : MIDP 2.0 Game API Core Concepts
- Java ME on Symbian OS : Building a Simple MIDP Game
- jQuery 1.3 : Compound effects & Creating custom animations
- jQuery 1.3 : Effects and speed
- Mobile Web Apps : Quick Wins (part 2) - Form Field Attributes
- Mobile Web Apps : Quick Wins (part 1) - Nifty Links
- iPhone Developer : Using Creative Popping Options
 
25 Inspiring Game of Thrones Quotes
 
Top 10
 
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
- Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
- Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
- Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
- Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
programming4us programming4us
 
Popular tags
 
Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8 BlackBerry Android Ipad Iphone iOS