eval(art);

UITableViewCell Templates

by Philippe Hausler on Aug.29, 2008, under Objective-C, iPhone/iPod Touch

Interface Builder by far is probably one of the easiest user interface design applications I have used. It is wonderful for rapid application design not only for Mac OS X but also iPhone. Unfortunately it still seems rather limited in some respects for the newly released iPhone SDK. Namely table cells still have to be hard programmed into your application and no layout management is directly available through Interface Builder.

This doesn’t have to be the case. There is a native UI object for UITableViewCells in Interface Builder just no way of directly formatting the table like the way Mac OS X applications are handled for tables.To get around this a subclass of UITableViews has to be made to allow a IBOutlet to a cell template.

@protocol AdvancedTableViewDelegate
@optional
- (void)tableView:(UITableView *)tableView didDeselectRowsFromTouches:(NSSet *)touches;
@end
 
@interface AdvancedTableView : UITableView {
	IBOutlet UITableViewCell *cellTemplate;
}
@property (nonatomic, retain) UITableViewCell *cellTemplate;
@end
@synthesize cellTemplate;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
	BOOL isCellTouch = NO;
	for(UITouch *aTouch in touches)
	{
		if(aTouch.view != self)
		{
			isCellTouch = YES;
		}
	}
	if(!isCellTouch)
	{
		if([self.delegate respondsToSelector:@selector(tableView:didDeselectRowsFromTouches:)])
		{
			[self.delegate performSelector:@selector(tableView:didDeselectRowsFromTouches:) withObject:self withObject:touches];
		}
	}
	[super touchesEnded:touches withEvent:event];
}

Furthermore, the native table view has no notification or delegate protocol to alert the delegate of a UITableView that the selection has changed to nothing. Since events bubble much like the concept of DOM events, the table views can capture the touch events and test to see if they are either to a cell or not. If the touch event is not a cell, most assuredly the event is indented to deselect the current table selection. Also accessor methods will need to be implemented to allow the table view to reference the assigned template from the xib.

@implementation  UITableViewCell (Copying)
- (id)copyWithZone:(NSZone *)zone
{
	UITableViewCell *copy = [[[self class] allocWithZone:zone] initWithFrame:self.frame reuseIdentifier:self.reuseIdentifier];
	copy.textAlignment = self.textAlignment;
	copy.image = self.image;
	copy.selectedImage = self.selectedImage;
 
	copy.accessoryType = self.accessoryType;
	copy.accessoryView = self.accessoryView;
 
	copy.indentationLevel = self.indentationLevel;
	copy.indentationWidth = self.indentationWidth;
 
	copy.textColor = self.textColor;
	copy.selectedTextColor = self.selectedTextColor;
 
	copy.shouldIndentWhileEditing = self.shouldIndentWhileEditing;
	copy.showsReorderControl = self.showsReorderControl;
	[copy addSubview:[self.contentView copy]];
 
	return copy;
 
}
@end
@implementation UIView (Copying)
- (id)copyWithZone:(NSZone *)zone
{
	return [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:self]];
}
- (id)firstSubviewOfClass:(Class)aClass
{
	for(id subView in self.subviews)
	{
		if([subView class] == aClass)
		{
			return subView;
		}
	}
	return NULL;
}
- (id)subviewAtIndex:(NSUInteger)index
{
	return [[self subviews] objectAtIndex:index];
}
@end

With these two extensions to the base views, table rows can now be rendered on the fly as copies of the original UITableViewCell template assigned to the table view. WARNING: Make sure to set the identifier in the UITableViewCell template object to the identifier specified in the table rendering code! Unfortunately any connections, bindings or actions will be lost in this copy so they will need to be remade programmatically.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
	NSInteger index = [indexPath indexAtPosition:1];
	static NSString *MyIdentifier = @"MyIdentifier";
 
	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
	if (cell == nil) {
		cell = [[(AdvancedTableView *)tableView cellTemplate] copy];
	}
	[[[cell subviewAtIndex:1] firstSubviewOfClass:[UILabel class]] setText:[testData objectAtIndex:index]];
	[[[cell subviewAtIndex:1] firstSubviewOfClass:[UIProgressView class]] setProgress:[[testProgressData objectAtIndex:index] floatValue]];
	return cell;
}

Since the cell creation and rendering method does not have the table passed as a subclass the object must be typeclassed. When referencing the subviews one must also be warry that the order of render defined in the xib file is going to most likely be the order that is in the actual rendered cell (this may not be the case if the cell has any children that are programmatically made). This method is also interoperable with the standard methods of creating cells, however if the cell is a subclass, any connections are lost.


Comments are closed.

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Visit our friends!

A few highly recommended friends...