Search code examples
iosobjective-ccocoa-touchcgpathcashapelayer

How to draw a filled path/shape in different colors


I need to color a shape on my screen any color I wish. I am currently trying to do this with a UIImage which I want to recolor to my desires. The only way to do this, as far as I know, is to take the individual pixels of the UIImage, which calls for many more lines of code that I would like to write to fix this problem. Is there any way to change the UIImage color other than what I have written? Would it be easier to draw the shape in context and fill it?

TY.

Update: The Hexagon is drawing itself the right way, but it doesn't fill right at all. Here is the code:

self.Hexagon.lineWidth=10;
[self.Hexagon moveToPoint:CGPointMake(hexWidth/2, 0)];
[self.Hexagon addLineToPoint:CGPointMake(hexWidth, hexHeight/4)];
[self.Hexagon addLineToPoint:CGPointMake(hexWidth, hexHeight*0.75)];
[self.Hexagon addLineToPoint:CGPointMake(hexWidth/2, hexHeight)];
[self.Hexagon addLineToPoint:CGPointMake(0, hexHeight*0.75)];
[self.Hexagon addLineToPoint:CGPointMake(0, hexHeight/4)];
[self.Hexagon addLineToPoint:CGPointMake(hexWidth/2, 0)];

[self.Hexagon closePath];
[[UIColor blackColor] setStroke];
[self.Hexagon stroke];

[[UIColor whiteColor] setFill];
[self.Hexagon fill];

Solution

  • I recommend using a CAShapeLayer. You could even animate the fill color. Your performance will be very good and your memory usage will be low.

    this function creates a CGPath containing a hexagon: (based on OP question)

    CGPathRef CGPathCreateHexagon( CGFloat hexWidth, CGFloat hexHeight )
    {
        CGMutablePathRef p = CGPathCreateMutable() ;
        CGPathMoveToPoint( p, NULL,  hexWidth * 0.5, 0.0 ) ;
        CGPathAddLineToPoint( p, NULL,  hexWidth, hexHeight * 0.75 ) ;
        CGPathAddLineToPoint( p, NULL, hexWidth, hexHeight * 0.75 ) ;
        CGPathAddLineToPoint( p, NULL, hexWidth * 0.5, hexHeight ) ;
        CGPathAddLineToPoint( p, NULL, 0.0, hexHeight * 0.75 ) ;
        CGPathAddLineToPoint( p, NULL, hexWidth * 0.5, 0.0 ) ;
        CGPathAddLineToPoint( p, NULL, 0.0, hexHeight * 0.25 ) ;
        CGPathAddLineToPoint( p, NULL, hexWidth * 0.5, 0.0 ) ;
        CGPathCloseSubpath( p ) ;
    
        return p ;
    }
    

    method to use when setting up your UIView:

    -(void)addHexagonLayer
    {        
        CAShapeLayer * layer = [ CAShapeLayer layer ] ;
        layer.lineWidth = 10.0 ;
        {
            CGPathRef p = CGPathCreateHexagon( 100.0, 100.0 ) ;
            layer.path = p ;
            CGPathRelease( p ) ;
        }       
        layer.fillColor = [[ UIColor redColor ] CGColor ] ; // put your fill color here
    
        layer.position = (CGPoint){ CGRectGetMidX( self.view.bounds ), CGRectGetMidY( self.view.bounds ) } ; // position your hexagon layer appropriately.
        [ self.view.layer addSublayer:layer ] ; // add layer to your view and position appropriately        
    
    }
    

    edit I created a complete demo just for fun:

    #import "AppDelegate.h"
    
    static CGPathRef CGPathCreateHexagon( CGAffineTransform * t, CGFloat w, CGFloat h )
    {
        CGFloat w_4 = w * 0.25 ;
        CGFloat w_2 = w * 0.5f ;
        CGFloat h_2 = h * 0.5f ;
    
        CGMutablePathRef p = CGPathCreateMutable() ;
        CGPathAddLines( p, t, (CGPoint[]){
            { -w_4, h_2 }, { w_4, h_2 }, { w_2, 0 }, { w_4, -h_2 }, { -w_4, -h_2 }, { -w_2, 0 }
        }, 6 ) ;
        CGPathCloseSubpath( p ) ;
    
        return p ;
    }
    
    @implementation CALayer (SetPositionPixelAligned)
    
    -(CGPoint)pixelAlignedPositionForPoint:(CGPoint)p
    {
        CGSize size = self.bounds.size ;
        CGPoint anchorPoint = self.anchorPoint ;
    
        CGPoint result = (CGPoint){ roundf( p.x ) + anchorPoint.x * fmodf( size.width, 2.0f ), roundf( p.y ) + anchorPoint.y * fmodf( size.height, 2.0f ) } ;
        return result;
    }
    
    @end
    
    @interface HexagonsView : UIView
    @property ( nonatomic ) CGFloat hexHeight ;
    @property ( nonatomic ) CGFloat hexWidth ;
    @property ( nonatomic, readonly ) CGPathRef hexagonPath ;
    @end
    
    @implementation HexagonsView
    @synthesize hexagonPath = _hexagonPath ;
    
    -(void)dealloc
    {
        CGPathRelease( _hexagonPath ) ;
        _hexagonPath = NULL ;
    }
    
    -(CGPathRef)hexagonPath
    {
        if ( !_hexagonPath )
        {
            _hexagonPath = CGPathCreateHexagon( NULL, self.hexWidth, self.hexHeight ) ;
        }
    
        return _hexagonPath ;
    }
    
    -(void)setHexWidth:(CGFloat)w
    {
        _hexWidth = w ;
        CGPathRelease( _hexagonPath ) ;
        _hexagonPath = NULL ;
    }
    
    -(void)setHexHeight:(CGFloat)h
    {
        _hexHeight = h ;
        CGPathRelease( _hexagonPath ) ;
        _hexagonPath = NULL ;
    }
    
    -(void)layoutSubviews
    {
        [ super layoutSubviews ] ;
        CGRect bounds = self.bounds ;
        bounds.size.height += self.hexHeight * 0.5 ;    // make sure we cover last row ;
    
        CGPoint p ;
        p.x = CGRectGetMinY( bounds ) ;
    
        while( p.y < CGRectGetMaxY( bounds ) )
        {
            p.x = CGRectGetMinX( bounds ) ;
            while( p.x < CGRectGetMaxX( bounds ) )
            {
                {
                    CAShapeLayer * layer = [ CAShapeLayer layer ] ;
                    layer.path = self.hexagonPath ;
                    layer.fillColor = [[ UIColor colorWithHue:(CGFloat)arc4random_uniform( 100 ) / 256.0  saturation:1.0 brightness:1.0 alpha:1.0 ] CGColor ]  ;
                    layer.position = [ layer pixelAlignedPositionForPoint:p ] ;
                    [ self.layer addSublayer:layer ] ;
                }
    
                CGPoint p2 = { p.x + self.hexWidth * 0.75f, p.y + self.hexHeight * 0.5f } ;
    
                if ( p2.y < CGRectGetMaxY( bounds ))    // no unnecessary hexagons
                {
                    CAShapeLayer * layer = [ CAShapeLayer layer ] ;
                    layer.path = self.hexagonPath ;
                    layer.fillColor = [[ UIColor colorWithHue:(CGFloat)arc4random_uniform( 256 ) / 256.0  saturation:1.0 brightness:1.0 alpha:1.0 ] CGColor ]  ;
                    layer.position = [ layer pixelAlignedPositionForPoint:p2 ] ;
                    [ self.layer addSublayer:layer ] ;
                }
    
                p.x += self.hexWidth * 1.5 ;
            }
            p.y += self.hexHeight ;
        }
    }
    
    @end
    
    @implementation AppDelegate
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        // Override point for customization after application launch.
        self.window.backgroundColor = [UIColor whiteColor];
    
        HexagonsView * view = [[ HexagonsView alloc ] initWithFrame:self.window.bounds ] ;
        view.hexHeight = 100.0f ;
        view.hexWidth = 100.0f ;
    
        [ self.window addSubview:view ] ;
    
        [self.window makeKeyAndVisible];
        return YES;
    }
    
    @end