Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

15~16day-Quartz2D(自定义控件、图片水印、裁剪以及屏幕截图)

$
0
0

前言

掌握
1、- (void)drawRect:(CGRect)rect;的使用
2、常见图形的绘制
3、绘图状态的设置:文字颜色、线宽
4、图形上下文状态的保持、恢复
5、图形上下文
6、矩阵操作
7、quartz 2D 的内存管理
8、图片水印、裁剪以及屏幕的截图

一、什么是quartz 2D

quartz 2D是一个二维绘图引擎,同时支持iOS、Mac系统

1、quartz 2D能完成的工作
1》绘制图形、文字
2》绘制、生成图片(图像)
3》读取、生成PDF文件
4》图片的裁截:圆形裁剪
5》自定义控件
2、quartz 2D在iOS开发中的价值
当使用uikit框架的普通控件无法实现需求的时候,可采用quartz 2D技术将 控件内部的结构画出来 ,自定义UI控件的样子

3、 Quartz2D须知

1)Quartz2D的API是纯C语言的
2)Quartz2D的API来自于Core Graphics框架
3)数据类型和函数基本都以CG作为前缀:CGContextRef、CGPathRef、CGContextStrokePath(ctx);
4、 Quartz 2D绘图的基础元素——路径

1)路径定义了一条或者或多条形状或子路径
2)子路径可以包含一条或者多条直线或曲线
3)子路径也可以是一些简单的形状,例如线、圆形、矩形或者星型等
4)子路径还可以包含复杂的形状,例如地图轮廓或者涂鸦等
5)路径可以是开放的,也可以是封闭的
6)路径主要使用在定义视图运动轨迹
4、quartz 2D 的内存管理
1、如果含有create、copy的函数创建对象,使用完之后必须释放,否则将导致内测泄露
2、如果retain了一个对象,不在使用时需将其release掉
1》可以使用quart 2D的函数(e g. CGColorSpaceRetain)来指定retain\release一个对象,或者使用core foundation 的CFRetain.

二、图形上下文(graphics context)

是一个CGContextRef类型数据
1、作用
1》保持绘图的信息
2》决定绘图的输出目标(PDF、bitmap、layer、printer、window)

这里写图片描述
p s:相同的一套绘图序列,指定不同的Graphics Context,就可将相同的图像绘制到不同的目标上

2、分类
Quartz2D提供了以下几种类型的Graphics Context:

Bitmap Graphics Context
PDF Graphics Context
Window Graphics Context
Layer Graphics Context
Printer Graphics Context

3、 图形上下文栈的操作

//将当前的上下文copy一份,保存到栈顶(那个栈叫做”图形上下文栈”)
void CGContextSaveGState(CGContextRef c)
//将栈顶的上下文出栈,替换掉当前的上下文
void CGContextRestoreGState(CGContextRef c)
- (void)drawRect:(CGRect)rect{

    CGContextRef context= UIGraphicsGetCurrentContext();

    //Pushes a copy of the current graphics state onto the graphics state stack for the context.

    CGContextSaveGState(context);

    //设置绘制信息

    UIBezierPath *path = [UIBezierPath bezierPath];

    [path moveToPoint:CGPointMake(10, 125)];

    [path addLineToPoint:CGPointMake(240, 125)];

    CGContextAddPath(context, path.CGPath);

    //设置绘图状态

    [[UIColor redColor] set];

    //渲染

    CGContextStrokePath(context);

    //绘制第二条线

    UIBezierPath *path1 = [UIBezierPath bezierPath];

    [path1 moveToPoint:CGPointMake(125, 10)];

    [path1 addLineToPoint:CGPointMake(125, 240)];



    CGContextAddPath(context, path1.CGPath);//Adds a previously created Quartz path object to the current path in a graphics context.

    //Sets the current graphics state to the state most recently saved.设置绘图状态为最近保存的上下文状态

    CGContextRestoreGState(context);

    CGContextStrokePath(context); 

}

三、自定义控件

1、如何利用Quartz2D绘制东西到view上?
(1)首先,得有图形上下文,因为它能保存绘图信息,并且决定着绘制到什么地方去
(2)其次,那个图形上下文必须跟view相关联,才能将内容绘制到view上面
2、自定义UI控件的步骤
1)新建一个类,继承自UIView
2)实现- (void)drawRect:(CGRect)rect方法,然后在这个方法中,可以:
(1)取得跟当前view相关联的图形上下文
(2)绘制相应的图形内容,绘制时产生的线条称为路径。 路径由一个或多个直线段或曲线段组成。
(3)利用图形上下文将绘制的所有内容渲染显示到view上面
也可以:利用UIKit封装的绘图函数直接绘图
3、drawRect:方法在什么时候被调用?
(1)当view第一次显示到屏幕上时(被加到UIWindow上显示出来)
(2)调用view的setNeedsDisplay或者setNeedsDisplayInRect:时
4、 drawRect:中取得的上下文(Layer Graphics Context)

在drawRect:方法中取得上下文后,就可以绘制东西到view上
1)View内部有个layer(图层)属性,drawRect:方法中取得的是一个Layer Graphics Context,因此,绘制的东西其实是绘制到view的layer上去了
2)View之所以能显示东西,完全是因为它内部的layer

四、quartz 2D 绘图的代码步骤

1.获得图形上下文

CGContextRef ctx = UIGraphicsGetCurrentContext();

2.拼接路径(下面代码是搞一条线段)


//新建一个起点
void CGContextMoveToPoint(CGContextRef c, CGFloat x, CGFloat y)
//添加新的线段到某个点
void CGContextAddLineToPoint(CGContextRef c, CGFloat x, CGFloat y)
//添加一个矩形
void CGContextAddRect(CGContextRef c, CGRect rect)
//添加一个椭圆
void CGContextAddEllipseInRect(CGContextRef context, CGRect rect)
//添加一个圆弧
void CGContextAddArc(CGContextRef c, CGFloat x, CGFloat y, CGFloat radius, CGFloat startAngle, CGFloat endAngle, int
clockwise)

3.绘制路径

//Mode参数决定绘制的模式
void CGContextDrawPath(CGContextRef c, CGPathDrawingMode mode)
//绘制空心路径
void CGContextStrokePath(CGContextRef c)
//绘制实心路径
void CGContextFillPath(CGContextRef c)

提示:一般以CGContextDraw、CGContextStroke、CGContextFill开头的函数,都是用来绘制路径的

4、案例 绘制下载进度条
这里写图片描述

#import "HSProgressView.h"




@interface HSProgressView ()

@property (nonatomic,weak) UILabel *textLable;




@end




@implementation HSProgressView




- (UILabel *)textLable{

    if (nil == _textLable) {

        UILabel *tmpView = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];

        _textLable = tmpView;

        [_textLable setTextAlignment:NSTextAlignmentCenter];

        [self addSubview:_textLable];

    }

    return _textLable;

}




//重写百分比的setter方法,来做一些额外的事情




- (void)setProgressValue:(CGFloat)progressValue{

    _progressValue = progressValue;

    [self.textLable setText:[NSString stringWithFormat:@"%.2f%%",progressValue*100]];

    //绘制进度图形
    /**
     在view 上做重绘的标志,当下次屏幕刷新的时候会调用drawRect方法

     */

    [self setNeedsDisplay];//重绘视图to notify the system that your view’s contents need to be redrawn.

}




/*绘制进度*/

// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

- (void)drawRect:(CGRect)rect {

    // Drawing code

    CGContextRef context = UIGraphicsGetCurrentContext();

    CGPoint center = CGPointMake(50, 50);

    CGFloat radious = 44;

    CGFloat startAngle = -M_PI_2;

    CGFloat endAngle = startAngle + self.progressValue * M_PI*2;

    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radious startAngle:startAngle endAngle:endAngle clockwise:YES];

    CGContextAddPath(context, path.CGPath);

    //渲染

    CGContextStrokePath(context);
}

五、图片水印、裁剪以及屏幕截图

1、图片水印

//1、开启一个基于位图的图形上下文
void     UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale);

//2.从上下文中取得图片(UIImage)

UIImage* UIGraphicsGetImageFromCurrentImageContext();

//3.结束基于位图的图形上下文
void     UIGraphicsEndImageContext();
/*例子*/
 //1、开启上下文

    UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);

    UIImage *image = [UIImage imageNamed:@"img"];

    [image drawAtPoint:CGPointZero];

    [@"@Lydia" drawAtPoint:CGPointMake(150, 150) withAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:12],NSForegroundColorAttributeName:[UIColor redColor]}];

    //2、获取上下文图片

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

    //3、结束上下文

    UIGraphicsEndImageContext();

    //4、显示图片

    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];

    [imageView setImage:newImage];

    [self.view addSubview:imageView];

    //5、保存图片

//    NSData *data = UIImageJPEGRepresentation(newImage, 0.0001);// value 1.0 represents the least compression (or best quality)

    NSData *data = UIImagePNGRepresentation(newImage);//将图片转换成二进制数据

    [data writeToFile:@"/Users/devzkn/Desktop/lydia.png" atomically:YES];

2、图片裁剪

这里写图片描述

void CGContextClip(CGContextRef c);//将当前上下所绘制的路径裁剪出来(超出这个裁剪区域的都不能显示)
 //方式二、裁剪感兴趣的部分

 UIRectClip(textFrame);//Modifies the current clipping path by intersecting it with the specified rectangle.
/*裁剪代码*/
#import "UIImage+Tool.h"

@implementation UIImage (Tool)

+ (UIImage *)imageWithName:(NSString *)name border:(CGFloat)border borderColor:(UIColor *)borderColor{

    UIImage *image = [UIImage imageNamed:name];//旧图片

    CGFloat borderWidth = border;//圆环高度

    //新图片的大小

    CGFloat newImageW = image.size.width+borderWidth*2;

    CGFloat newImageH = image.size.height+borderWidth*2;

    CGFloat circleW = (newImageH>newImageW) ? newImageW:newImageH;//新的图片尺寸



    //1、开启上下文

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(circleW, circleW), NO, 0.0);

    //1>画大圆正切于矩形-----------

    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, circleW,circleW)];

    //获取当前上下文

    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextAddPath(context, path.CGPath);

    [borderColor set];

    CGContextFillPath(context);

    //绘制小圆

    //2>画一个正切于旧图片的小圆-----

    CGRect clipR = CGRectMake(borderWidth,borderWidth,image.size.width,image.size.height);

    UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:clipR];

    [clipPath addClip];//设置裁剪区域

    [image drawAtPoint:CGPointMake(borderWidth, borderWidth)];

    //2、获取上下文图片

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();



    //3、结束上下文

    UIGraphicsEndImageContext();

    return newImage;   

}

3、屏幕截图

核心代码:调用某个view的layer的renderInContext:方法即可
- (void)renderInContext:(CGContextRef)ctx;
UIGraphicsBeginImageContext(self.view.bounds.size);

   CGContextRef context = UIGraphicsGetCurrentContext();

   //将layer渲染到上下文

   [self.view.layer renderInContext:context];

   //获取新图片

   UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

   NSData *data = UIImagePNGRepresentation(newImage);

   [data writeToFile:@"/Users/devzkn/Desktop/layer.png" atomically:YES];

正文

一、案例
1、饼图

这里写图片描述

/*pie

 寻找规律

 */

// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

- (void)drawRect:(CGRect)rect {

    // Drawing code

    CGContextRef context = UIGraphicsGetCurrentContext();

    NSArray *data = @[@25,@25,@50];

    CGPoint center = CGPointMake(125, 125);

    CGFloat radious = 100;//半径

    CGFloat startAngle = 0;//起始弧度

    CGFloat endAngle = 0;

    CGFloat angle = 0;

    //开始绘制饼图

    for (NSNumber *num in data) {

        //绘制

        angle =num.floatValue/100.0 *M_PI*2;

        startAngle = endAngle;//上一个饼图的结束弧度为下一个饼图的开始弧度

        endAngle = startAngle + angle;

        UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:center radius:radious startAngle:startAngle endAngle:endAngle clockwise:YES];

        [path2 addLineToPoint:center];

        CGContextAddPath(context, path2.CGPath);

        [[UIColor randomColor]set];

        //渲染第i个

        CGContextFillPath(context);

    }



}




- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    [self setNeedsDisplay];

}

//生成随机颜色
+ (UIColor *)randomColor{

    /*

     RGB 24位,RGB 每个颜色通道8位,8 的二进制255,即颜色取值是0~255

     RGBA

     */

    CGFloat red = arc4random_uniform(256)/255.0;

    CGFloat blue = arc4random_uniform(256)/255.0;

    CGFloat green = arc4random_uniform(256)/255.0;

    return  [UIColor colorWithRed:red green:green blue:blue alpha:1];

}

2、柱状图
这里写图片描述

/* bar*/

// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

- (void)drawRect:(CGRect)rect {

    // Drawing code

    CGContextRef context = UIGraphicsGetCurrentContext();

    NSArray *data = @[@25,@25,@50];

    CGFloat ViewH =CGRectGetHeight(self.frame);

    CGFloat width = CGRectGetWidth(self.frame)/(2*data.count-1);

    for (int i =0; i<data.count; i++) {

        //绘制

        CGFloat height = ViewH*([(NSNumber *)data[i] floatValue]/100.0);

        CGFloat x = i*width*2;

        CGFloat y = ViewH - height ;

        UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(x, y, width, height)];

        CGContextAddPath(context, path.CGPath);

        [[UIColor randomColor] set];

        //渲染

        CGContextFillPath(context);

    }  
}

3、UIKit的演练

1》- (void)drawInRect:(CGRect)rect withAttributes:(NSDictionary<NSString *,id> *)attrs
/*

 字符属性



 字符属性可以应用于 attributed string 的文本中。



 NSString *const NSFontAttributeName;(字体)



 NSString *const NSParagraphStyleAttributeName;(段落)



 NSString *const NSForegroundColorAttributeName;(字体颜色)



 NSString *const NSBackgroundColorAttributeName;(字体背景色)



 NSString *const NSLigatureAttributeName;(连字符)



 NSString *const NSKernAttributeName;(字间距)



 NSString *const NSStrikethroughStyleAttributeName;(删除线)



 NSString *const NSUnderlineStyleAttributeName;(下划线)



 NSString *const NSStrokeColorAttributeName;(边线颜色)



 NSString *const NSStrokeWidthAttributeName;(边线宽度)



 NSString *const NSShadowAttributeName;(阴影)(横竖排版)



 NSString *const NSVerticalGlyphFormAttributeName;



 常量



 1> NSFontAttributeName(字体)



 该属性所对应的值是一个 UIFont 对象。该属性用于改变一段文本的字体。如果不指定该属性,则默认为12-point Helvetica(Neue)。



 2> NSParagraphStyleAttributeName(段落)



 该属性所对应的值是一个 NSParagraphStyle 对象。该属性在一段文本上应用多个属性。如果不指定该属性,则默认为 NSParagraphStyle 的defaultParagraphStyle 方法返回的默认段落属性。



 3> NSForegroundColorAttributeName(字体颜色)



 该属性所对应的值是一个 UIColor 对象。该属性用于指定一段文本的字体颜色。如果不指定该属性,则默认为黑色。



 4> NSBackgroundColorAttributeName(字体背景色)



 该属性所对应的值是一个 UIColor 对象。该属性用于指定一段文本的背景颜色。如果不指定该属性,则默认无背景色。



 5> NSLigatureAttributeName(连字符)



 该属性所对应的值是一个 NSNumber 对象(整数)。连体字符是指某些连在一起的字符,它们采用单个的图元符号。0 表示没有连体字符。1 表示使用默认的连体字符。2表示使用所有连体符号。默认值为 1(注意,iOS 不支持值为 2)。



 6> NSKernAttributeName(字间距)



 该属性所对应的值是一个 NSNumber 对象(整数)。字母紧排指定了用于调整字距的像素点数。字母紧排的效果依赖于字体。值为 0 表示不使用字母紧排。默认值为07> NSStrikethroughStyleAttributeName(删除线)



 该属性所对应的值是一个 NSNumber 对象(整数)。该值指定是否在文字上加上删除线,该值参考“Underline Style Attributes”。默认值是NSUnderlineStyleNone。



 8> NSUnderlineStyleAttributeName(下划线)



 该属性所对应的值是一个 NSNumber 对象(整数)。该值指定是否在文字上加上下划线,该值参考“Underline Style Attributes”。默认值是NSUnderlineStyleNone。



 9> NSStrokeColorAttributeName(边线颜色)



 该属性所对应的值是一个 UIColor 对象。如果该属性不指定(默认),则等同于 NSForegroundColorAttributeName。否则,指定为删除线或下划线颜色。更多细节见“Drawing attributedstrings that are both filled and stroked”。



 10> NSStrokeWidthAttributeName(边线宽度)



 该属性所对应的值是一个 NSNumber 对象(小数)。该值改变描边宽度(相对于字体size 的百分比)。默认为 0,即不改变。正数只改变描边宽度。负数同时改变文字的描边和填充宽度。例如,对于常见的空心字,这个值通常为3.011> NSShadowAttributeName(阴影)
 该属性所对应的值是一个 NSShadow 对象。默认为 nil。
 12> NSVerticalGlyphFormAttributeName(横竖排版)
 该属性所对应的值是一个 NSNumber 对象(整数)。0 表示横排文本。1 表示竖排文本。在 iOS 中,总是使用横排文本,0 以外的值都未定义。
 */

4、雪花

这里写图片描述

- (void)drawRect:(CGRect)rect{

    if (self.snowY >= CGRectGetHeight(self.frame)) {

        self.snowY = 0;

    }

    self.snowY++;

    UIImage *image = [UIImage imageNamed:@"雪花"];

    [image drawAtPoint:CGPointMake(0, self.snowY)];

}




- (void)awakeFromNib{

    /*

     1.NSTimer的精确度就显得低了点,比如NSTimer的触发时间到的时候,runloop如果在阻塞状态,触发时间就会推迟到下一个runloop周期--》导致任务的叠加。

     2.CADisplayLink使用场合相对专一,适合做UI的不停重绘,比如自定义动画引擎或者视频播放的渲染。NSTimer的使用范围要广泛的多,各种需要单次或者循环定时处理的任务都可以使用。在UI相关的动画或者显示内容使用 CADisplayLink比起用NSTimer的好处就是我们不需要在格外关心屏幕的刷新频率了,因为它本身就是跟屏幕刷新同步的。

     */

//    [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(setNeedsDisplay) userInfo:nil repeats:YES];

    /*

     IOS设备的屏幕刷新频率是固定的,CADisplayLink在正常情况下会在每次刷新结束都被调用,精确度相当高。

     */

    CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(setNeedsDisplay)];

    [link addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

}

5、矩阵操作

- (void)drawRect:(CGRect)rect{

    CGContextRef context = UIGraphicsGetCurrentContext();

    //平移上下文

    CGContextTranslateCTM(context, 50, 100);

    CGContextRotateCTM(context, M_PI_4);

    CGContextScaleCTM(context, 0.2, 0.7);

    //上下文矩阵操作,之后在设置绘图信息(路径)

    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(-50, -100, 120, 100)];

    CGContextAddPath(context, path.CGPath);

    [[UIColor yellowColor]set];

    CGContextStrokePath(context);

}

6、手势密码

//

//  HSLockView.m

//  20140621-手势解锁

//

//  Created by devzkn on 4/21/16.

//  Copyright © 2016 hisun. All rights reserved.

//




#import "HSLockView.h"




@interface HSLockView ()

@property (nonatomic,strong) NSMutableArray *btns;//存储选中按钮

@property (nonatomic,assign) CGPoint pos;//当前手指的位置










@end

@implementation HSLockView

- (NSMutableArray *)btns{

    if (nil == _btns) {

        _btns = [NSMutableArray array];

    }

    return _btns;

}




#if 0

- (void)awakeFromNib{

    //绘制九宫格

}

#endif




- (instancetype)initWithCoder:(NSCoder *)aDecoder{



    self = [super initWithCoder:aDecoder];

    if (self) {

        //绘制九宫格按钮

        [self addBtns];

    }

    return self;



}




/**

 绘制九宫格

 */

- (void)addBtns{

    UIImage *normalImage = [UIImage imageNamed:@"gesture_node_normal"];

    UIImage *highlightedImage = [UIImage imageNamed:@"gesture_node_highlighted"];




    for (int i=0; i<9; i++) {

        //绘制按钮

        UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];

        //设置共性信息

        [btn setTag:i];//用于标记按钮

        [btn setImage:normalImage forState:UIControlStateNormal];

        [btn setImage:highlightedImage forState:UIControlStateSelected];//选中状态要手动管理

        /**

         处理按钮点击事件:

         方式一:addTarget:self

         方式二;touches ,设置按钮为不可交互,交给VIew进行处理(事件的传递原理)

         */

        //添加监听方法,进行性手动管理选中状态

//        [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchDown];

        [btn setUserInteractionEnabled:NO];




        [self addSubview:btn];

    }

}




#pragma  mark - 点击事件

#if 0

- (void)clickBtn:(UIButton*)btn{

    [btn setSelected:YES];

}

#endif




#pragma  mark - 使用touches方法 进行按钮的选中处理




/** 获取当前触摸点的坐标*/

- (CGPoint) getPosWithTouches:(NSSet *)touches{

    if (touches == nil) {

        return CGPointZero;

    }

    UITouch *touch = [touches anyObject];//Returns one of the objects in the set, or nil if the set contains no objects.

    return [touch locationInView:self];//当前触摸点

}




/** 选中按钮的获取*/

- (UIButton *) getButtonWithPos:(CGPoint)pos{

    //计算选中范围

    CGRect selectRect;

    UIButton *posBtn ;

    CGFloat wh =30;

    for (UIButton *btn in self.subviews) {

        CGFloat x = btn.center.x-wh*0.5;

        CGFloat y = btn.center.y- wh *0.5;

        selectRect = CGRectMake(x, y, wh, wh);

        BOOL flg = CGRectContainsPoint(selectRect, pos);

        if (flg) {

             posBtn = btn;

            break;

        }

    }

    return posBtn;

}




#pragma mark - 手指抬起的处理

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    //判断是否解锁成功

    NSMutableString *answerStr = [[NSMutableString alloc]init];

    for (UIButton *btn in self.btns) {

        [answerStr appendFormat:@"%d",btn.tag];

    }

    NSLog(@"%@",answerStr);

    //去除选中状态

    [self.btns makeObjectsPerformSelector:@selector(setSelected:) withObject:NO];

    //清空轨迹

    [self.btns removeAllObjects];

    [self setNeedsDisplay];

}




/**控制按钮的选择状态 */

#if 0

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{



}

#endif




/** 存储选中按钮*/

- (void) touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    CGPoint pos = [self getPosWithTouches:touches];

    [self setPos:pos];

    UIButton *btn = [self getButtonWithPos:pos];

    if (btn && btn.selected == NO) {

        [btn setSelected:YES];

        if (![self.btns containsObject:btn]) {

            [self.btns addObject:btn];//存储选中按钮

        }

    }

    [self setNeedsDisplay];//绘制




}










#pragma mark - 画线




- (void)drawRect:(CGRect)rect{

    if (self.btns.count == 0) {

        return;

    }

    //开始绘制

    UIBezierPath *path = [UIBezierPath bezierPath];

    for (int i=0; i<self.btns.count; i++) {

        UIButton *btn = self.btns[i];

        if (i==0) {

            [path moveToPoint:btn.center];

        }else{

            [path addLineToPoint:btn.center];

        }

    }



    [path addLineToPoint:self.pos];

    //设置绘图状态

    [[UIColor greenColor]set];

    [path  setLineWidth:10];

    [path setLineCapStyle:kCGLineCapRound];

    [path  setLineJoinStyle:kCGLineJoinRound];



    [path stroke];

}










#pragma mark - layoutSubviews 设置子视图的位置信息




- (void)layoutSubviews{

    [super layoutSubviews];

    //设置位置信息

    CGFloat btnWidth = 74;

    CGFloat btnHeight = 74;

    CGFloat x = 0;

    CGFloat y = 0;

    int col = 0;//所在的列数

    int row = 0;//所在的行数

    int tolCol = 3;//总列数

    CGFloat viewWidth = CGRectGetWidth(self.frame);

    CGFloat viewHeight = CGRectGetHeight(self.frame);

    CGFloat rowMargin = (viewWidth - tolCol*btnWidth)*0.25;//间距

    CGFloat colMargin = (viewHeight - tolCol*btnHeight)*0.25;//间距




    for (int i =0; i<9; i++) {

        //绘制

        UIButton *btn = self.subviews[i];

        //计算所在的列数

        col = i % tolCol;

        row = i / tolCol;//所在行数

        x = col*(rowMargin+btnWidth)+rowMargin;//x 与所在的列相关

        y = row*(colMargin+btnHeight)+colMargin;

        [btn setFrame:CGRectMake(x, y, btnWidth, btnHeight)];
    }
}
@end

7、画板

1》从相册选择照片
从相册选择照片 Expand source

//
//  ViewController.m
//  20160421-画板
//
//  Created by devzkn on 4/21/16.
//  Copyright © 2016 hisun. All rights reserved.
//

#import "ViewController.h"
#import "HSPaintView.h"
#import "HSHandleImageView.h"


@interface ViewController ()<UIImagePickerControllerDelegate,UINavigationControllerDelegate>
@property (weak, nonatomic) IBOutlet HSPaintView *paintView;

@end

@implementation ViewController
- (IBAction)lineWidthValueChange:(UISlider *)sender {
    [self.paintView setLineWidth:sender.value];
}
- (IBAction)colorClick:(UIButton *)sender {
    [self.paintView setColor:sender.backgroundColor];

}
#pragma mark - 清屏
- (IBAction)cleanScreen:(UIBarButtonItem *)sender {
    [self.paintView cleanScreen];

}
- (IBAction)undo:(UIBarButtonItem *)sender {
    [self.paintView undo];
}
- (IBAction)eraserClick:(UIBarButtonItem *)sender {
    [self.paintView setColor:[UIColor whiteColor]];
}
#pragma mark - 从相册选择照片
- (IBAction)selectedPicture:(UIBarButtonItem*)sender {
    //去用户相册
    UIImagePickerController *pickerVc = [[UIImagePickerController alloc]init];
    /*
     UIImagePickerControllerSourceTypePhotoLibrary,相簿,默认值
     UIImagePickerControllerSourceTypeCamera,//真机测试
     UIImagePickerControllerSourceTypeSavedPhotosAlbum
     */
    [pickerVc setSourceType:UIImagePickerControllerSourceTypeSavedPhotosAlbum];//照片
    //modal
    //设置代理
    [pickerVc setDelegate:self];
    [self presentViewController:pickerVc animated:YES completion:^{
        //
    }];

}
#pragma mark - UIImagePickerControllerDelegate
// The picker does not dismiss itself; the client dismisses it in these callbacks.
// The delegate will receive one or the other, but not both, depending whether the user
// confirms or cancels.
#if 0
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(nullable NSDictionary<NSString *,id> *)editingInfo{// NS_DEPRECATED_IOS(2_0, 3_0)
    NSLog(@"%s",__func__);
}
#endif
/**
 选中图片的时候调用
 */
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{
    /**
     {
     UIImagePickerControllerMediaType = "public.image";
     UIImagePickerControllerOriginalImage = "<UIImage: 0x7ae83b30> size {3000, 2002} orientation 0 scale 1.000000";
     UIImagePickerControllerReferenceURL = "assets-library://asset/asset.JPG?id=ED7AC36B-A150-4C38-BB8C-B6D696F4F2ED&ext=JPG";
     }
     */
    UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
    HSHandleImageView *handleView = [[HSHandleImageView alloc]initWithFrame:self.paintView.frame];
    /*
     回调,进行传值,定义block的具体内容
     */
    [handleView setBlock:^(UIImage *image){
        [self.paintView setImage:image];
    }];
    [handleView setImage:image];//将选中的图片显示到handleView
    [self.view addSubview:handleView];
    [self dismissViewControllerAnimated:YES completion:^{
        //
    }];

}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{
    NSLog(@"%s",__func__);
    [self dismissViewControllerAnimated:YES completion:^{
        //
    }];
}

#pragma mark 保存图片
- (IBAction)save:(UIBarButtonItem *)sender {
    [self.paintView save];

}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

@end

2》画板的基本功能
画板的辅助功能

//

//  HSPaintView.m

//  20160421-画板

//

//  Created by devzkn on 4/21/16.

//  Copyright © 2016 hisun. All rights reserved.

//




#import "HSPaintView.h"

#import "HSPaintPath.h"

#import "MBProgressHUD+MJ.h"




@interface HSPaintView ()

@property (nonatomic,strong) HSPaintPath *path;//存储当前触摸的路径

@property (nonatomic,strong) NSMutableArray *paths;//存储历史的路径







@end




@implementation HSPaintView




- (void)setImage:(UIImage *)image{

    _image = image;

    [self.paths addObject:image];

    [self setNeedsDisplay];



}




- (void)cleanScreen{

    [self.paths removeAllObjects];

    [self setNeedsDisplay];



}

- (void)undo{

    [self.paths removeLastObject];

    [self setNeedsDisplay];



}




- (void)setLineWidth:(CGFloat)lineWidth{

    _lineWidth = lineWidth;

}




- (void) setColor:(UIColor *)color{

    _color = color;

}




- (NSMutableArray *)paths{

    if (nil == _paths) {

        _paths = [NSMutableArray array];

    }

    return _paths;

}




/**     获取触摸点*/

- (CGPoint) pointWithTouches:(NSSet *)touches{

    UITouch *touch = [touches anyObject];

    return [touch locationInView:self];

}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    self.path = [HSPaintPath pathWithColor:self.color lineWidht:self.lineWidth startPoint:[self pointWithTouches:touches]];

    [self.paths addObject:self.path];

}

-(void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    //确定终点

    [self.path addLineToPoint:[self pointWithTouches:touches]];

    [self setNeedsDisplay];

}

#pragma mark - 绘制状态

- (void)drawRect:(CGRect)rect{

    if (!self.paths.count) {

        return;

    }

    for (HSPaintPath *path in self.paths) {

        if ([path  isKindOfClass:[UIImage class]]) {

            //画图片

            UIImage *image = (UIImage *)path;

            [image drawAtPoint:CGPointZero];

        }else{

            //设置绘制状态

            [path.color set];

            //描边

            [path stroke];

        }

    }

}




#pragma mark -初始化绘制状态

- (void)awakeFromNib{

    self.lineWidth = 2;

}




#pragma mark - 保存到用户相册

- (void)save{

    //1.截屏

    UIGraphicsBeginImageContext(self.bounds.size);

    //渲染

    //获取上下文

    CGContextRef context = UIGraphicsGetCurrentContext();

    [self.layer renderInContext:context];

    //2.获取新图片

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();

    //3.关闭

    UIGraphicsEndImageContext();

    //4\保存

//        NSData *data = UIImagePNGRepresentation(newImage);

//        [data writeToFile:@"/Users/devzkn/Desktop/layer.png" atomically:YES];

    //保存到用户相册里面

    UIImageWriteToSavedPhotosAlbum(newImage, self, @selector(image:didFinishSavingWithError:contextInfo:), context);   

}




- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{

    NSLog(@"%s",__func__);

    if (error) {

        //保存失败

        [MBProgressHUD showError:@"保存失败"];




    }else{

        [MBProgressHUD showSuccess:@"保存成功"];

    }

}

3》手势识别器的处理
手势识别器的使用

#if 1

- (instancetype)initWithFrame:(CGRect)frame{

    self = [super initWithFrame:frame];

    if (self) {

        //设置自己的属性

        [self addUILongPressGestureRecognizer];

        [self addUIRotationGestureRecognizer];

        [self addUIPinchGestureRecognizer];



    }

    return self;

}

#endif




#pragma mark - 添加手势识别器

- (void) addUILongPressGestureRecognizer{

    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)];

    [longPress setDelegate:self];

    [self.imageView addGestureRecognizer:longPress];

}




- (void) addUIRotationGestureRecognizer{

    UIRotationGestureRecognizer *rotationGR = [[UIRotationGestureRecognizer alloc]initWithTarget:self action:@selector(rotationGR:)];

    [rotationGR setDelegate:self];

    [self.imageView addGestureRecognizer:rotationGR];

}




#pragma mark - rotation

- (void) rotationGR:(UIRotationGestureRecognizer*)rotationGR{

    NSLog(@"%s",__func__);

    [self.imageView setTransform:CGAffineTransformRotate(self.imageView.transform, rotationGR.rotation)];

    [rotationGR setRotation:0];



}




- (void) addUIPinchGestureRecognizer{

    UIPinchGestureRecognizer *pinchGR = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(pinchGR:)];

    [pinchGR setDelegate:self];

    [self.imageView addGestureRecognizer:pinchGR];}







- (void) pinchGR:(UIPinchGestureRecognizer*)pinchGR{

    NSLog(@"%s",__func__);

    [self.imageView setTransform:CGAffineTransformScale(self.imageView.transform, pinchGR.scale, pinchGR.scale)];

    [pinchGR setScale:1];





}




#pragma mark - 长按处理

- (void) longPress:(UILongPressGestureRecognizer*)longPressGR{

    if (longPressGR.state == UIGestureRecognizerStateEnded) {

        //动画

        [UIView animateWithDuration:0.5 animations:^{

            //设置透明度

            [self.imageView setAlpha:0.3];

        } completion:^(BOOL finished) {

            //还原透明度

            [UIView animateWithDuration:0.5 animations:^{

                [self.imageView setAlpha:1.0];

            }completion:^(BOOL finished) {

                //1、截屏

                UIGraphicsBeginImageContext(self.bounds.size);

                [self.layer renderInContext:UIGraphicsGetCurrentContext()];

                UIImage *newImage =UIGraphicsGetImageFromCurrentImageContext();

                UIGraphicsEndImageContext();

                //2.将图片传递给VC,进行渲染--使用block

                self.block(newImage);

                //3销毁自己

                [self removeFromSuperview];

            }];

        }];



    }

}




- (void)setImage:(UIImage *)image{

    _image = image;

    [self.imageView setImage:image];

}

#pragma mark - gestureRecognizerDelegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{

    return YES;

}

总结

一、编程规范
1、枚举类型变量通常以K开头
枚举定义 Expand source

/* Line cap styles. */

typedef CF_ENUM(int32_t, CGLineCap) {
    kCGLineCapButt,

    kCGLineCapRound,

    kCGLineCapSquare
};

二、语法
1、字符串格式
1》转义符号%

[self.textLable setText:[NSString stringWithFormat:@"%.2f%%",progressValue*100]];
作者:u011018979 发表于2017/7/2 14:38:45 原文链接
阅读:33 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>