37

I am trying to develop a small application. In it, I need to detect the color of a pixel within an UIView. I have a CGPoint defining the pixel I need. The colors of the UIView are changed using CoreAnimation.

I know there are some complex ways to extract color information from UIImages. However I couldn't find a solution for UIViews.

In Pseudo-Code I am looking for something like

pixel = [view getPixelAtPoint:myPoint];
UIColor *mycolor = [pixel getColor];

Any input greatly appreciated.

2
  • Did you find a (fast) solution? Commented Oct 11, 2009 at 1:42
  • Using the bitmap context turned out to be fast enough for my purposes. Give it a try and see how it works for you.
    – 0x90
    Commented Oct 15, 2009 at 2:03

5 Answers 5

81

Here is more efficient solution:

// UIView+ColorOfPoint.h
@interface UIView (ColorOfPoint)
- (UIColor *) colorOfPoint:(CGPoint)point;
@end

// UIView+ColorOfPoint.m
#import "UIView+ColorOfPoint.h"
#import <QuartzCore/QuartzCore.h>

@implementation UIView (ColorOfPoint)

- (UIColor *) colorOfPoint:(CGPoint)point
{
    unsigned char pixel[4] = {0};

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef context = CGBitmapContextCreate(pixel, 1, 1, 8, 4, colorSpace, kCGBitmapAlphaInfoMask & kCGImageAlphaPremultipliedLast);

    CGContextTranslateCTM(context, -point.x, -point.y);

    [self.layer renderInContext:context];

    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);

    //NSLog(@"pixel: %d %d %d %d", pixel[0], pixel[1], pixel[2], pixel[3]);

    UIColor *color = [UIColor colorWithRed:pixel[0]/255.0 green:pixel[1]/255.0 blue:pixel[2]/255.0 alpha:pixel[3]/255.0];

    return color;
}

@end

Link to files: https://github.com/ivanzoid/ikit/tree/master/UIView+ColorOfPoint

9
  • Warning in .m, removed with #import <QuartzCore/QuartzCore.h>
    – Jonny
    Commented Dec 8, 2011 at 16:18
  • It looks like you are rendering just a single pixel. Very cool.
    – mahboudz
    Commented Jan 22, 2012 at 19:04
  • Can this code be modified to work out the color of a whole row of pixels? Commented Feb 12, 2013 at 11:40
  • Could you explain CGContextTranslateCTM(context, -point.x, -point.y);? Commented Aug 15, 2013 at 20:04
  • 1
    @ivanzoid I've added a swift translation. Feel free to copy Commented Nov 15, 2014 at 12:01
27

A swift'ified version of ivanzoid's answer

Swift 3

extension CALayer {

    func colorOfPoint(point:CGPoint) -> CGColor {

        var pixel: [CUnsignedChar] = [0, 0, 0, 0]

        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)

        let context = CGContext(data: &pixel, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)

        context!.translateBy(x: -point.x, y: -point.y)

        self.render(in: context!)

        let red: CGFloat   = CGFloat(pixel[0]) / 255.0
        let green: CGFloat = CGFloat(pixel[1]) / 255.0
        let blue: CGFloat  = CGFloat(pixel[2]) / 255.0
        let alpha: CGFloat = CGFloat(pixel[3]) / 255.0

        let color = UIColor(red:red, green: green, blue:blue, alpha:alpha)

        return color.cgColor
    }
}

Swift 2

extension CALayer {

    func colorOfPoint(point:CGPoint)->CGColorRef
    {
        var pixel:[CUnsignedChar] = [0,0,0,0]

        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue)

        let context = CGBitmapContextCreate(&pixel, 1, 1, 8, 4, colorSpace, bitmapInfo.rawValue)

        CGContextTranslateCTM(context, -point.x, -point.y)

        self.renderInContext(context!)

        let red:CGFloat = CGFloat(pixel[0])/255.0
        let green:CGFloat = CGFloat(pixel[1])/255.0
        let blue:CGFloat = CGFloat(pixel[2])/255.0
        let alpha:CGFloat = CGFloat(pixel[3])/255.0

        let color = UIColor(red:red, green: green, blue:blue, alpha:alpha)

        return color.CGColor
    }

}

based around the underlying CALayer but easily translatable back to UIView.

1
  • 4
    i tried to use it but it returns just zeros. so i always get white color. unfortunately my programming skills are not so advanced to find out myself. thank you
    – Lachtan
    Commented Jun 10, 2016 at 17:07
11

Swift 4 & 4.2. Tested with XCode 10, iOS 12, Simulator & iPhone 6+ Running iOS 12.1.

This code work fine.

extension UIView {
    func colorOfPoint(point: CGPoint) -> UIColor {
        let colorSpace: CGColorSpace = CGColorSpaceCreateDeviceRGB()
        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue)

        var pixelData: [UInt8] = [0, 0, 0, 0]

        let context = CGContext(data: &pixelData, width: 1, height: 1, bitsPerComponent: 8, bytesPerRow: 4, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)

        context!.translateBy(x: -point.x, y: -point.y)

        self.layer.render(in: context!)

        let red: CGFloat = CGFloat(pixelData[0]) / CGFloat(255.0)
        let green: CGFloat = CGFloat(pixelData[1]) / CGFloat(255.0)
        let blue: CGFloat = CGFloat(pixelData[2]) / CGFloat(255.0)
        let alpha: CGFloat = CGFloat(pixelData[3]) / CGFloat(255.0)

        let color: UIColor = UIColor(red: red, green: green, blue: blue, alpha: alpha)

        return color
    }
}
8

It is pretty horrible and slow. Basically you create a bitmap context with a backing store you allocate so you can read the memory, then you render the views layer in the context and read the appropriate point in ram.

If you know how to do it for an image already you can do something like this:

- (UIImage *)imageForView:(UIView *)view {
  UIGraphicsBeginImageContext(view.frame.size);
  [view.layer renderInContext: UIGraphicsGetCurrentContext()];
  UIImage *retval = UIGraphicsGetImageFromCurrentImageContext(void);
  UIGraphicsEndImageContext();

  return retval;
}

And then you will get an image where you can get the pixel data. I am sure the mechanism you have for dealing with images involves rendering them into a context anyway, so you can merge that with this and factor out the actual image creation. So if you take that could and remove the bit where you load the image and replace it with the context render:

[view.layer renderInContext: UIGraphicsGetCurrentContext()];

you should be good.

2

Fixed some minor errors

- (UIImage *)imageForView:(UIView *)view {
    UIGraphicsBeginImageContext(view.frame.size);
    [view.layer renderInContext: UIGraphicsGetCurrentContext()];
    UIImage *retval = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return retval;
}

Also, add

#import <QuartzCore/QuartzCore.h>  

to your file to avoid warning messages.

Combining your code with the code found here works flawlessly.

2
  • Why does it look like you simply copied Louis's code and removed the superfluous "void" in the call to UIGraphicsGetImageFromCurrentImageContext()? Commented Oct 28, 2010 at 4:58
  • 1
    That is what I did. What is a better way to handle corrections on here?
    – 0x90
    Commented Oct 28, 2010 at 21:33

Not the answer you're looking for? Browse other questions tagged or ask your own question.