UITableView is the most used and versatile control in iPhone applications. It is very flexible and sometimes you may not even recognize it within the UI, but it is there and does the job. The customization I describe below is based on an actual customer request. It is more about general views mechanic but I used it in the context of UITableView and so describe it here.
As you may already know the primary content of UITableView is a list of cells. And cells may be grouped in sections, and every section may define a custom header view. If sections contain few cells then several section headers may be visible at the same time. That was the case in the aforementioned request. Additionally, header views were composite and had two labels: the primary title and the secondary informational text. The essence of the request was to show informational text only for the topmost visible section header.
The key to the solution is the didMoveToWindow method defined in UIView. It is called every time a UIView is added to views hierarchy or removed from it. The rest is simple: implement this method in section header view and when it is called find out a new topmost visible header. To track which view is the one I add complementaryHeader property:
@interface HeaderView : UIView {
NSString *text;
NSString *subtext;
BOOL complementaryHeader;
}
@property(retain) NSString *text;
@property(retain) NSString *subtext;
@property(assign) BOOL complementaryHeader;
@end
Every time section header view is added or removed from window I sort the list of visible views by y coordinate and set this flag for all views except the first one. I also tell views to redraw themselves. In drawRect method the header view checks if this flag is set and does not show informational text if so.
NSComparisonResult CompareHeaderViewsByY(
HeaderView *view1, HeaderView *view2, void *context) {
const CGFloat diff = view2.frame.origin.y - view1.frame.origin.y;
return diff > 0 ? NSOrderedAscending
: diff < 0 ? NSOrderedDescending : NSOrderedSame;
}
@implementation HeaderView
@synthesize text, subtext, complementaryHeader;
+ (NSMutableArray *)visibleViews {
static NSMutableArray *views;
if (!views) {
views = [[NSMutableArray alloc] init];
}
return views;
}
- (void)didMoveToWindow {
[super didMoveToWindow];
NSMutableArray *views = [HeaderView visibleViews];
if (self.window) {
if (![views containsObject:self]) {
[views addObject:self];
}
} else {
[views removeObject:self];
}
[views sortUsingFunction:CompareHeaderViewsByY context:NULL];
HeaderView *upperView = nil;
for (HeaderView *view in views) {
if (upperView && EqualStrings(view.text, upperView.text)) {
view.complementaryHeader = YES;
} else {
view.complementaryHeader = NO;
}
[view setNeedsDisplay];
upperView = view;
}
}
- (void)dealloc {
[text release];
[subtext release];
[super dealloc];
}
- (void)drawRect:(CGRect)rect {
[[UIColor colorWithWhite:0.7 alpha:1] set];
UIRectFill(self.bounds);
if (text) {
[[UIColor blackColor] set];
[text drawAtPoint:CGPointMake(10, 5) withFont:[UIFont systemFontOfSize:14]];
}
if (subtext && !complementaryHeader) {
[[UIColor grayColor] set];
[subtext drawAtPoint:CGPointMake(10, 22) withFont:[UIFont systemFontOfSize:12]];
}
}
@end
Source code in sample Xcode project is available here.







