How to Make Custom Drawn Gradient Backgrounds in a Grouped UITableView with Core Graphics

In iPhone app design, it is important to find a balance between customization and familiarity.  Many times, subtle differences are all you need to make yourself stand out from the crowd.  While doing the Ambiance 3.0 redesign, I wanted something to make my list of content a little different than all the other cookie cutter table views out there.

A great place to start is to implement your own background and use a clear table view background.  Many apps are now doing this and it is come to be expected from a grouped UITableView that is done with a little love.  Another way to spruce up these tables is to add a custom background color.  Since 3.0, this has been as easy as setting the cells backgroundColor property.  I implemented both of these and it wasn't enough for me. It still looked like so many apps out there.  I thought about putting a gradient view as the background, only to find that setting a grouped UITableViewCell's background ignored the nice rounded corners of the cell.  Almost giving up, I googled around to see if anybody else has done it and shared some code.

This was a great resource as it helped me in the right direction — Core Graphics.  It was meant as a way to customize the background of a cell before the 3.0 sdk came out, but I used it as a basis for my code to draw a gradient!  It needed tweaking and optimization, but after a little bit of hacking away at it, I was able to get it to work nicely.  I present to you the UACellBackgroundView .

To use the UACellBackgroundView, simply add the .h and .m files to your project, then use it as the cell background view.

.h file
//
// UACellBackgroundView.h
// Ambiance
//
// Created by Matt Coneybeare on 1/31/10.
// Copyright 2010 Urban Apps LLC. All rights reserved.
//

#import <Foundation/Foundation.h>

#import <UIKit/UIKit.h>

typedef enum {
    UACellBackgroundViewPositionSingle = 0 ,
    UACellBackgroundViewPositionTop ,
    UACellBackgroundViewPositionBottom ,
    UACellBackgroundViewPositionMiddle
} UACellBackgroundViewPosition ;

@interface UACellBackgroundView : UIView {
    UACellBackgroundViewPosition position ;
}

@property ( nonatomic ) UACellBackgroundViewPosition position ;

@end

view raw gistfile1.m This Gist brought to you by GitHub .
.m file
//
// UACellBackgroundView.m
// Ambiance
//
// Created by Matt Coneybeare on 1/31/10.
// Copyright 2010 Urban Apps LLC. All rights reserved.
//

#define TABLE_CELL_BACKGROUND { 1, 1, 1, 1, 0.866, 0.866, 0.866, 1} // #FFFFFF and #DDDDDD
#define kDefaultMargin 10

#import "UACellBackgroundView.h"

static void addRoundedRectToPath ( CGContextRef context , CGRect rect , float ovalWidth , float ovalHeight );

@implementation UACellBackgroundView

@synthesize position ;

- ( BOOL ) isOpaque {
    return NO ;
}

- ( void ) drawRect: ( CGRect ) aRect {
    // Drawing code

    CGContextRef c = UIGraphicsGetCurrentContext ();

    int lineWidth = 1 ;

    CGRect rect = [ self bounds ];
    CGFloat minx = CGRectGetMinX ( rect ), midx = CGRectGetMidX ( rect ), maxx = CGRectGetMaxX ( rect );
    CGFloat miny = CGRectGetMinY ( rect ), midy = CGRectGetMidY ( rect ), maxy = CGRectGetMaxY ( rect );
    miny -= 1 ;

    CGFloat locations [ 2 ] = { 0.0 , 1.0 };
    CGColorSpaceRef myColorspace = CGColorSpaceCreateDeviceRGB ();
    CGGradientRef myGradient = nil ;
    CGFloat components [ 8 ] = TABLE_CELL_BACKGROUND ;
    CGContextSetStrokeColorWithColor ( c , [[ UIColor grayColor ] CGColor ]);
    CGContextSetLineWidth ( c , lineWidth );
    CGContextSetAllowsAntialiasing ( c , YES );
    CGContextSetShouldAntialias ( c , YES );
    
    if ( position == UACellBackgroundViewPositionTop ) {

        miny += 1 ;

CGMutablePathRef path = CGPathCreateMutable ();
        CGPathMoveToPoint ( path , NULL , minx , maxy );
        CGPathAddArcToPoint ( path , NULL , minx , miny , midx , miny , kDefaultMargin );
        CGPathAddArcToPoint ( path , NULL , maxx , miny , maxx , maxy , kDefaultMargin );
        CGPathAddLineToPoint ( path , NULL , maxx , maxy );
CGPathAddLineToPoint ( path , NULL , minx , maxy );
CGPathCloseSubpath ( path );

// Fill and stroke the path
CGContextSaveGState ( c );
CGContextAddPath ( c , path );
CGContextClip ( c );

myGradient = CGGradientCreateWithColorComponents ( myColorspace , components , locations , 2 );
CGContextDrawLinearGradient ( c , myGradient , CGPointMake ( minx , miny ), CGPointMake ( minx , maxy ), 0 );

CGContextAddPath ( c , path );
CGPathRelease ( path );
CGContextStrokePath ( c );
CGContextRestoreGState ( c );

    } else if ( position == UACellBackgroundViewPositionBottom ) {
        
        CGMutablePathRef path = CGPathCreateMutable ();
        CGPathMoveToPoint ( path , NULL , minx , miny );
        CGPathAddArcToPoint ( path , NULL , minx , maxy , midx , maxy , kDefaultMargin );
        CGPathAddArcToPoint ( path , NULL , maxx , maxy , maxx , miny , kDefaultMargin );
        CGPathAddLineToPoint ( path , NULL , maxx , miny );
        CGPathAddLineToPoint ( path , NULL , minx , miny );
CGPathCloseSubpath ( path );

// Fill and stroke the path
CGContextSaveGState ( c );
CGContextAddPath ( c , path );
CGContextClip ( c );

myGradient = CGGradientCreateWithColorComponents ( myColorspace , components , locations , 2 );
CGContextDrawLinearGradient ( c , myGradient , CGPointMake ( minx , miny ), CGPointMake ( minx , maxy ), 0 );

CGContextAddPath ( c , path );
CGPathRelease ( path );
CGContextStrokePath ( c );
CGContextRestoreGState ( c );


    } else if ( position == UACellBackgroundViewPositionMiddle ) {

CGMutablePathRef path = CGPathCreateMutable ();
CGPathMoveToPoint ( path , NULL , minx , miny );
CGPathAddLineToPoint ( path , NULL , maxx , miny );
CGPathAddLineToPoint ( path , NULL , maxx , maxy );
CGPathAddLineToPoint ( path , NULL , minx , maxy );
CGPathAddLineToPoint ( path , NULL , minx , miny );
CGPathCloseSubpath ( path );

// Fill and stroke the path
CGContextSaveGState ( c );
CGContextAddPath ( c , path );
CGContextClip ( c );

myGradient = CGGradientCreateWithColorComponents ( myColorspace , components , locations , 2 );
CGContextDrawLinearGradient ( c , myGradient , CGPointMake ( minx , miny ), CGPointMake ( minx , maxy ), 0 );

CGContextAddPath ( c , path );
CGPathRelease ( path );
CGContextStrokePath ( c );
CGContextRestoreGState ( c );

    } else if ( position == UACellBackgroundViewPositionSingle ) {
miny += 1 ;

CGMutablePathRef path = CGPathCreateMutable ();
        CGPathMoveToPoint ( path , NULL , minx , midy );
        CGPathAddArcToPoint ( path , NULL , minx , miny , midx , miny , kDefaultMargin );
        CGPathAddArcToPoint ( path , NULL , maxx , miny , maxx , midy , kDefaultMargin );
        CGPathAddArcToPoint ( path , NULL , maxx , maxy , midx , maxy , kDefaultMargin );
        CGPathAddArcToPoint ( path , NULL , minx , maxy , minx , midy , kDefaultMargin );
CGPathCloseSubpath ( path );


// Fill and stroke the path
CGContextSaveGState ( c );
CGContextAddPath ( c , path );
CGContextClip ( c );


myGradient = CGGradientCreateWithColorComponents ( myColorspace , components , locations , 2 );
CGContextDrawLinearGradient ( c , myGradient , CGPointMake ( minx , miny ), CGPointMake ( minx , maxy ), 0 );

CGContextAddPath ( c , path );
CGPathRelease ( path );
CGContextStrokePath ( c );
CGContextRestoreGState ( c );
    }

    CGColorSpaceRelease ( myColorspace );
    CGGradientRelease ( myGradient );
    return ;
}

- ( void ) dealloc {
    [ super dealloc ];
}

- ( void ) setPosition: ( UACellBackgroundViewPosition ) newPosition {
    if ( position != newPosition ) {
        position = newPosition ;
        [ self setNeedsDisplay ];
    }
}

@end

static void addRoundedRectToPath ( CGContextRef context , CGRect rect , float ovalWidth , float ovalHeight ) {
    float fw , fh ;

    if ( ovalWidth == 0 || ovalHeight == 0 ) { // 1
        CGContextAddRect ( context , rect );
        return ;
    }

    CGContextSaveGState ( context ); // 2

    CGContextTranslateCTM ( context , CGRectGetMinX ( rect ), // 3
                                    CGRectGetMinY ( rect ));
    CGContextScaleCTM ( context , ovalWidth , ovalHeight ); // 4
    fw = CGRectGetWidth ( rect ) / ovalWidth ; // 5
    fh = CGRectGetHeight ( rect ) / ovalHeight ; // 6

    CGContextMoveToPoint ( context , fw , fh / 2 ); // 7
    CGContextAddArcToPoint ( context , fw , fh , fw / 2 , fh , 1 ); // 8
    CGContextAddArcToPoint ( context , 0 , fh , 0 , fh / 2 , 1 ); // 9
    CGContextAddArcToPoint ( context , 0 , 0 , fw / 2 , 0 , 1 ); // 10
    CGContextAddArcToPoint ( context , fw , 0 , fw , fh / 2 , 1 ); // 11
    CGContextClosePath ( context ); // 12

    CGContextRestoreGState ( context ); // 13
}

view raw gistfile1.mm This Gist brought to you by GitHub .

Whenever you want to use it, just set the cell's backgroundView to be the UACellBackgroundView in your own UITableViewCell Subclass's initWithStyle  method.  Then, when you have your data and are creating cells, simply set the position of the cell by calling setPosition, of just call the line directly if you don't want to subclass the UITableViewCell.  The position is important as it lets the cell know to draw itself correctly depending on whether or not the cell is first, last, in the middle, or a single cell.

- ( id ) initWithStyle: ( UITableViewCellStyle ) style reuseIdentifier: ( NSString * ) identifier {
    if ( self = [ super initWithStyle: UITableViewCellStyleSubtitle reuseIdentifier: identifier ]) {

        // Background Image
        self . backgroundView = [[[ UACellBackgroundView alloc ] initWithFrame: CGRectZero ] autorelease ];
    }
    return self ;
}

- ( void ) setPosition: ( UACellBackgroundViewPosition ) newPosition {
    [( UACellBackgroundView * ) self . backgroundView setPosition: newPosition ];
}


view raw gistfile1.m This Gist brought to you by GitHub .

Hope this helps you in your quest for a better app.  If you want to use it, you will have to set your cell labels and elements to have a clear background, so hopefully you have a fast drawing method and have optimized your table view cells to do minimum work during scrolling.

posted on 2011-04-28 14:41  GaryGaryGary  阅读(189)  评论(0)    收藏  举报

导航