zoukankan      html  css  js  c++  java
  • iOS获取UIView上某点的颜色值

    项目需求中遇到获取UIView上某个坐标点的RGB颜色值的需求,现在把自己找到的解决方案简单总结记录一下,遇到了下面的情况:

    不可移动的UIView

    如下图所示,有一个圆形的颜色板,当手指在颜色板上移动时,UIViewControllerbackgroundColor将会设置成手指在颜色板上触点的颜色值:

    不可移动的颜色板

    实现该功能的方案搜索至stackoverflow, 出处点击这里.

    核心代码如下:

    #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, (CGBitmapInfo)kCGImageAlphaPremultipliedLast);
    
        CGContextTranslateCTM(context, -point.x, -point.y);
    
        [self.layer renderInContext:context];
    
        CGContextRelease(context);
        CGColorSpaceRelease(colorSpace);
    
        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;
    }

    该功能封装成UIView的一个Category了,主要注意头文件QuartzCore.h的引入以及坐标系的转换.

    最终,当手指在颜色板上滑动时,获取ColorPanel中颜色值的调用如下:

    #import "ImmovableColorPanel.h"
    #import "UIView+ColorOfPoint.h"
    
    @implementation ImmovableColorPanel
    
    - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
        CGPoint location = [touch locationInView:self];
        self.color = [self colorOfPoint:location];
        [self sendActionsForControlEvents:UIControlEventValueChanged];
        return YES;
    }
    
    @end

    旋转式的UIView

    现在假设上图所示的颜色板是可旋转的,获取颜色值的方式修改为获取色环中间正上方某点的颜色值,如下图所示,获取的是下三角所指某点的颜色值(下三角不会随着颜色板的旋转而旋转).

    可旋转的颜色板

    先看看颜色板随着手指的滑动而旋转是如何实现的:

    #import "RoundColorPanel.h"
    
    @interface RoundColorPanel ()
    
    /**
     *  ColorPanel中心点的坐标值
     */
    @property (nonatomic) CGPoint centerPoint;
    
    @end
    
    @implementation RoundColorPanel
    
    - (void)awakeFromNib {
        [super awakeFromNib];
    
        self.centerPoint = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
    }
    
    - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
    
        CGPoint previousLocation = [touch previousLocationInView:self];
        CGPoint location = [touch locationInView:self];
        CGFloat previousRadian = [self radianToCenterPoint:self.centerPoint withPoint:previousLocation];
        CGFloat curRadian = [self radianToCenterPoint:self.centerPoint withPoint:location];
        CGFloat changedRadian = curRadian - previousRadian;
        [self rotateByRadian:changedRadian];
    
        [self sendActionsForControlEvents:UIControlEventValueChanged];
        return YES;
    }
    
    /**
     *  以ColorPanel的anchorPoint为坐标原点建立坐标系,计算坐标点|point|与坐标原点的连线距离x轴正方向的夹角
     *
     *  @param centerPoint 坐标原点坐标
     *  @param point       某坐标点
     *
     *  @return 坐标点|point|与坐标原点的连线距离x轴正方向的夹角
     */
    - (CGFloat)radianToCenterPoint:(CGPoint)centerPoint withPoint:(CGPoint)point {
        CGVector vector = CGVectorMake(point.x - centerPoint.x, point.y - centerPoint.y);
        return atan2f(vector.dy, vector.dx);
    }
    
    /**
     *  将图层旋转radian弧度
     *
     *  @param radian 旋转的弧度
     */
    - (void)rotateByRadian:(CGFloat)radian {
        CGAffineTransform transform = self.layer.affineTransform;
        transform = CGAffineTransformRotate(transform, radian);
        self.layer.affineTransform = transform;
    }

    核心代码就是rotateByRadian:消息,使用CALayer的仿射变换完成。

    可能你会发现,在continueTrackingWithTouch:消息中没有看到UIView的分类消息colorOfPoint:的发送,其实如果你尝试这样做,会发现这样取出的颜色值是旋转开始时下三角所指点referPoint的颜色值,随着ColorPanel的旋转,该颜色值不会改变,这是因为我们对ColorPanel的旋转是通过仿射变换实现,而仿射变换会改变图层的坐标系,也就是说,referPoint现在所处的坐标系是已做过仿射变换的坐标系,而不再是以图层左上角为坐标原点,向右为x轴正方向,向下为y轴正方向的坐标系。当然你可以计算ColorPanel总的旋转角度totalChangedRadian,然后通过使referPoint旋转-totalChangedRadian角度来计算该点在新坐标系中的坐标值,如下代码所示:

    #import "RoundColorPanel.h"
    #import "UIView+ColorOfPoint.h"
    
    @interface RoundColorPanel ()
    
    /**
     *  颜色板中心点坐标
     */
    @property (nonatomic) CGPoint centerPoint;
    /**
     *  获取颜色值参考点与|centerPoint|的连线与x轴正方向的夹角
     */
    @property (nonatomic) CGFloat referRadian;
    /**
     *  获取颜色值参考点与|centerPoint|的连线长度
     */
    @property (nonatomic) CGFloat referRadius;
    /**
     *  颜色板在整个旋转过程中的总和
     */
    @property (nonatomic) CGFloat totalChangedRadian;
    
    @end
    
    @implementation RoundColorPanel
    
    - (void)awakeFromNib {
        [super awakeFromNib];
    
        self.centerPoint = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
        //|referPoint|获取该点的颜色值
        CGPoint referPoint = CGPointMake(CGRectGetMidX(self.bounds), 2);
        CGVector vector = CGVectorMake(referPoint.x - self.centerPoint.x, referPoint.y - self.centerPoint.y);
        self.referRadian = atan2(vector.dy, vector.dx);
        self.referRadius = sqrt(vector.dx * vector.dx + vector.dy * vector.dy);
        self.layer.cornerRadius = CGRectGetWidth(self.bounds) / 2;
    }
    
    - (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
    
        CGPoint previousLocation = [touch previousLocationInView:self];
        CGPoint location = [touch locationInView:self];
        CGFloat previousRadian = [self radianToCenterPoint:self.centerPoint withPoint:previousLocation];
        CGFloat curRadian = [self radianToCenterPoint:self.centerPoint withPoint:location];
        CGFloat changedRadian = curRadian - previousRadian;
        [self rotateByRadian:changedRadian];
    
        self.totalChangedRadian += changedRadian;
        CGFloat radian = self.referRadian - self.totalChangedRadian;
        CGPoint referPoint1 = CGPointMake(self.centerPoint.x + self.referRadius * cos(radian), self.centerPoint.y + self.referRadius * sin(radian));
        self.color = [self colorOfPoint:referPoint1];
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    
        return YES;
    }
    
    - (CGFloat)radianToCenterPoint:(CGPoint)centerPoint withPoint:(CGPoint)point {
        CGVector vector = CGVectorMake(point.x - centerPoint.x, point.y - centerPoint.y);
        return atan2f(vector.dy, vector.dx);
    }
    
    - (void)rotateByRadian:(CGFloat)radian {
        CGAffineTransform transform = self.layer.affineTransform;
        transform = CGAffineTransformRotate(transform, radian);
        self.layer.affineTransform = transform;
    }

    不过,我更趋向于使用接下来使用的方法,更加简单和理解。

    RoundColorPanel放入一个容器视图RotaryColorPanel中,RoundColorPanel的旋转改变的是自身视图的坐标系,而不会改变其父视图的坐标系,因此我们可以在其父视图的坐标系中定义需要取颜色值的坐标点,代码如下:

    #import "RotaryColorPanel.h"
    #import "RoundColorPanel.h"
    #import "UIView+ColorOfPoint.h"
    #import "XXNibBridge.h"
    
    @interface RotaryColorPanel () <XXNibBridge>
    
    /**
     *  可旋转的颜色板
     */
    @property (weak, nonatomic) IBOutlet RoundColorPanel *roundColorPanel;
    /**
     *  下三角指示标识
     */
    @property (weak, nonatomic) IBOutlet UIImageView *indicator;
    
    /**
     *  获取该点的颜色值
     */
    @property (nonatomic) CGPoint referPoint;
    
    @end
    
    @implementation RotaryColorPanel
    
    - (void)awakeFromNib {
        [super awakeFromNib];
    
        self.referPoint = CGPointMake(CGRectGetMidX(self.indicator.frame), CGRectGetMaxY(self.indicator.frame));
        [self.roundColorPanel addTarget:self action:@selector(colorPanelRotated:) forControlEvents:UIControlEventValueChanged];
    }
    
    - (void)colorPanelRotated:(id)sender {
        UIColor *color = [self colorOfPoint:self.referPoint];
        if (self.colorChangedHandler) {
            self.colorChangedHandler(color);
        }
    }
    
    @end

    获取颜色值的调用变成如下:

    #import "RotaryColorPanelViewController.h"
    #import "RotaryColorPanel.h"
    
    @interface RotaryColorPanelViewController ()
    
    @property (weak, nonatomic) IBOutlet RotaryColorPanel *rotaryColorPanel;
    
    @end
    
    @implementation RotaryColorPanelViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        __weak typeof(self) weakSelf = self;
        self.rotaryColorPanel.colorChangedHandler = ^(UIColor *color) {
            weakSelf.view.backgroundColor = color;
        };
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    @end

    滑条式UIView

    该样式与上面的区别是,在颜色条上有遮挡物滑块,获取颜色值的点坐标是滑块中心点的坐标, 如下图所示:

    滑条式颜色板

    解决方案同第2中情况类似,就是让滑块和颜色板处在并列的层级,它们同时直属于同一superview中。

    上述所有代码已上传github,可点击此处获取.

  • 相关阅读:
    Django框架简介
    Django初识
    web前端 html
    python基础概念
    线程
    进程线程区别,和线程初识
    Nginx配置多个服务共用80端口
    Supervisor重新加载配置
    Gunicorn-Django部署
    django.db.utils.OperationalError: (1193, "Unknown system variable 'storage_engine'")
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/5105900.html
Copyright © 2011-2022 走看看