最终效果图:
UIImage分类,Point2Color:
//
// UIImage+Point2Color.h
// 26_popOverCtrl
//
// Created by beyond on 14-8-31.
// Copyright (c) 2014年 com.beyond. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIImage (Point2Color)
// 传入 一个点坐标,返回图片上该点的颜色对象
- (UIColor *)colorFromPoint:(CGPoint)point;
@end
//
// UIImage+Point2Color.m
// 26_popOverCtrl
//
// Created by beyond on 14-8-31.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 分类,传入一个point,获取该点的颜色
#import "UIImage+Point2Color.h"
@implementation UIImage (Point2Color)
// 传入 一个点坐标,返回图片上该点的颜色对象
// 将图片写入内存,再依据【点】中取颜色
- (UIColor *)colorFromPoint:(CGPoint)point
{
UIColor *color = nil;
// 得到取色图片的引用
CGImageRef inImage = self.CGImage;
// 调用自定义方法:从_imgView里面的image的引用,创建并返回对应的上下文
CGContextRef contexRef = [self ARGBBitmapContextFromImage:inImage];
// 如果创建该图片对应的上下文失败
if (contexRef == NULL){
NSLog(@"取色图片--创建对应的上下文失败~");
return nil;
}
// 准备将【取色图片】写入刚才创建出来的上下文
size_t w = CGImageGetWidth(inImage);
size_t h = CGImageGetHeight(inImage);
CGRect rect = {{0,0},{w,h}};
// 调试输出rect:--{{0, 0}, {225, 250}}
// 将位图写入(渲染)已经创建好的 上下文工作空间
CGContextDrawImage(contexRef, rect, inImage);
// 得到位图上下文 内存数据块的首地址,用指针记住,作为基地址
unsigned char* dataPoint = CGBitmapContextGetData (contexRef);
NSLog(@"----首地址,指针%p",dataPoint);
// ----首地址,指针0x8b3f000
if (dataPoint != NULL) {
//offset 即:根据触摸点的xy,定位到位图内存空间中的一个特定像素
//4 的意思是每一个像素点,占4个字节
// w是每一行所有点的总数
// 根据所在行,所在列,算出在内存块中的偏移地址,然后乘以4,因为每一个点在内存中占四个字节
int offset = 4*((w*round(point.y))+round(point.x));
// alpha 为内存基地址+偏移地址
int alpha = dataPoint[offset];
// red 为内存基地址+偏移地址+1 其他类似
int red = dataPoint[offset+1];
int green = dataPoint[offset+2];
int blue = dataPoint[offset+3];
NSLog(@"偏移地址: %i colors: RGBA %i %i %i %i",offset,red,green,blue,alpha);
// offset: 150908 colors: RGB A 255 0 254 255
// 根据RGBA 生成颜色对象
color = [UIColor colorWithRed:(red/255.0f) green:(green/255.0f) blue:(blue/255.0f) alpha:(alpha/255.0f)];
}
// 操作完成后,释放上下文对象
CGContextRelease(contexRef);
// 从内存中释放掉 加载到内存的图像数据
if (dataPoint) { free(dataPoint); }
// 返回图片上该点对应的颜色
return color;
}
// 自定义方法2:通过_imgView里面的image的引用,创建并返回对应的上下文,即根据CGImageRef来创建一个ARGBBitmapContext
- (CGContextRef)ARGBBitmapContextFromImage:(CGImageRef) inImage
{
// 要创建的上下文
CGContextRef context = NULL;
// 色彩空间
CGColorSpaceRef colorSpace;
// 位图数据在内存空间的首地址
void * bitmapData;
// 每一行的字节数
int bitmapBytesPerRow;
// 图片总的占的字节数
int bitmapByteCount;
// 得到图片的宽度和高度,将要使用整个图片,创建上下文
size_t pixelsWide = CGImageGetWidth(inImage);
size_t pixelsHigh = CGImageGetHeight(inImage);
// 每一行占多少字节. 本取色图片中的每一个像素点占4个字节;
// 红 绿 蓝 透明度 各占一个字节(8位 取值范围0~255)
// 每一行的字节数,因为每一个像素点占4个字节(包含RGBA)(其中一个R就是一个字节,占8位,取值是2的8次方 0~255)
bitmapBytesPerRow = (pixelsWide * 4);
// 图片总的占的字节数
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
// 使用指定的 色彩空间(RGB)
colorSpace = CGColorSpaceCreateDeviceRGB();
if (colorSpace == NULL)
{
fprintf(stderr, "创建并分配色彩空间 出错\n");
return NULL;
}
// 为取色图片数据 分配所有的内存空间
// 所有画到取色图片上下文的操作,都将被渲染到此内存空间
bitmapData = malloc( bitmapByteCount );
if (bitmapData == NULL)
{
fprintf (stderr, "内存空间分配失败~");
CGColorSpaceRelease( colorSpace );
return NULL;
}
// 创建位图上下文. 使用 pre-multiplied ARGB, ARGB中的每一个成员都占8个bit位,即一字节,一个像素共占4个字节
// 无论原取色图片的格式是什么(CMYK或Grayscale),都将通过CGBitmapContextCreate方法,转成指定的ARGB格式
context = CGBitmapContextCreate (bitmapData,
pixelsWide,
pixelsHigh,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
(CGBitmapInfo)kCGImageAlphaPremultipliedFirst);
if (context == NULL)
{
free (bitmapData);
fprintf (stderr, "位图上下文创建失败~");
}
// 在返回上下文之前 必须记得释放 色彩空间
CGColorSpaceRelease( colorSpace );
return context;
}
@end
ColorPicker控制器及其代理
//
// ColorPickerController.h
// 26_popOverCtrl
//
// Created by beyond on 14-8-31.
// Copyright (c) 2014年 com.beyond. All rights reserved.
//
#import <UIKit/UIKit.h>
@protocol ColorPickerDelegate;
@interface ColorPickerController : UIViewController
// 成员:代理,到时个通知该代理,选择的颜色是啥~
@property (weak, nonatomic) id<ColorPickerDelegate> delegate;
@end
//
// ColorPickerController.m
// 26_popOverCtrl
//
// Created by beyond on 14-8-31.
// Copyright (c) 2014年 com.beyond. All rights reserved.
//
#import "ColorPickerController.h"
// 分类
#import "UIImage+Point2Color.h"
// 颜色选择完毕,通知代理
#import "ColorPickerDelegate.h"
@interface ColorPickerController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ColorPickerController
#pragma mark - 生命周期方法
- (void)viewDidLoad
{
[super viewDidLoad];
// 重要~~~指定 当前控制器在popover中显示的大小(跟 图片 一样)
self.preferredContentSize = self.imageView.image.size;
}
// 触摸结束时,获取点击的坐标,调用UIImage分类方法,获取图片上该点的颜色
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// 有人需要该点的颜色,才进行取色
if ([self.delegate respondsToSelector:@selector(colorPickerController:didSelectedColor:)]) {
// 获得触摸点
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:touch.view];
// 获得颜色
UIColor *color = [self.imageView.image colorFromPoint:point];
// 告诉代理,该点对应的颜色
[self.delegate colorPickerController:self didSelectedColor:color];
}
}
@end
//
// ColorPickerDelegate.h
// 26_popOverCtrl
//
// Created by beyond on 14-8-31.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 颜色选择控制器的代理,当它解码出用户点击处的颜色时,通知代理
#import <Foundation/Foundation.h>
@class ColorPickerController;
@protocol ColorPickerDelegate <NSObject>
@optional
- (void)colorPickerController:(ColorPickerController *)ctrl didSelectedColor:(UIColor *)color;
@end
主控制器
//
// BeyondViewController.m
// 26_popOverCtrl
//
// Created by beyond on 14-8-31.
// Copyright (c) 2014年 com.beyond. All rights reserved.
// 00000000
// 99999999
#import "BeyondViewController.h"
// 点击左边Item,弹出Nana控制器
#import "NanaViewController.h"
// 点击中间的按钮,弹出颜色选择控制器
#import "ColorPickerController.h"
// 代理方法
#import "ColorPickerDelegate.h"
@interface BeyondViewController ()<ColorPickerDelegate>
- (IBAction)menuClick:(UIBarButtonItem *)sender;
- (IBAction)colorButtonClick:(UIButton *)sender;
// UIPopoverController 不能是局部变量,必须是成员变量
@property (nonatomic, strong) UIPopoverController *menuPopover;
// UIPopoverController 不能是局部变量,必须是成员变量
@property (nonatomic, strong) UIPopoverController *colorPopover;
@end
@implementation BeyondViewController
#pragma mark - 懒加载 getter方法
- (UIPopoverController *)menuPopover
{
if (_menuPopover == nil) {
// 1.创建内容控制器
NanaViewController *nana = [[NanaViewController alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:nana];
// 2.创建popover
self.menuPopover = [[UIPopoverController alloc] initWithContentViewController:nav];
}
// 返回popOver
return _menuPopover;
}
- (UIPopoverController *)colorPopover
{
if (_colorPopover == nil) {
// 1.创建内容控制器
ColorPickerController *cpvc = [[ColorPickerController alloc] init];
cpvc.delegate = self;
// 2.创建popover
self.colorPopover = [[UIPopoverController alloc] initWithContentViewController:cpvc];
// 重要~~~点击popOver之外的阴影,使点击事件可以穿透...
// self.colorPopover.passthroughViews = @[self.colorButton];
}
return _colorPopover;
}
#pragma mark - 颜色选择控制器的代理方法
- (void)colorPickerController:(ColorPickerController *)cpvc didSelectedColor:(UIColor *)color
{
self.view.backgroundColor = color;
}
#pragma mark - 监听按钮点击
/**
* 点击菜单,在指定位置,弹出popOver
*/
- (IBAction)menuClick:(UIBarButtonItem *)sender
{
// 显示到哪个位置
[self.menuPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
/**
* 点击了颜色按钮,在指定位置,弹出popOver
*/
- (IBAction)colorButtonClick:(UIButton *)sender
{
[self.colorPopover presentPopoverFromRect:sender.bounds inView:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
@end