简体   繁体   中英

How to get the color of a pixel in an UIView?

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.

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

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 .

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.

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.

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
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM