给UIView绘图的时候,一定要给它设一个背景颜色(包括clearColor,它也是一个颜色),不然绘图会很乱
#import "LockView.h"
@interface LockView ()
/**
* 当前选中的按钮
*/
@property (nonatomic, strong) NSMutableArray *selectBtnArray;
/**
* 当前手指所在的点
*/
@property (nonatomic, assign) CGPoint curP;
@end
@implementation LockView
- (NSMutableArray *)selectBtnArray {
if (_selectBtnArray == nil) {
_selectBtnArray = [NSMutableArray array];
}
return _selectBtnArray;
}
- (void)awakeFromNib {
[super awakeFromNib];
//添加按钮
[self setUp];
}
- (instancetype)initWithFrame:(CGRect)frame {
if ( self = [super initWithFrame:frame]) {
//添加按钮
[self setUp];
}
return self;
}
//添加按钮
- (void)setUp {
//创建按钮
for (int i = 0; i <9; i++) {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.userInteractionEnabled = NO;
btn.tag = i;
[btn setImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];
[btn setImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected];
[self addSubview:btn];
}
}
//给定一个集合,获取当前手指的点
- (CGPoint)getCurPintWithTouches:(NSSet *)touches {
//如果点在按钮上,让按钮成为选中状态
//获取当前手指所在的点
UITouch *touch = [touches anyObject];
CGPoint curP = [touch locationInView:self];
return curP;
}
/**
* 给定一个点,判断点在不在按钮身上.
*
* @param point 指定的点
*
* @return 当前点所在的按钮, 该值有可能为nil. 点不在按钮身上为nil
*/
- (UIButton *)btnRectContainsPoint:(CGPoint)point {
for (UIButton *btn in self.subviews) {
//判断一个点在不在指定的区域当中
if (CGRectContainsPoint(btn.frame, point)) {
return btn;
break;
}
}
return nil;
}
//开始点击
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//如果点在按钮上,让按钮成为选中状态
//获取当前手指所在的点
CGPoint curP = [self getCurPintWithTouches:touches];
UIButton *btn = [self btnRectContainsPoint:curP];
if (btn && btn.selected == NO) {
//保存当前选中的按钮
[self.selectBtnArray addObject:btn];
btn.selected = YES;
}
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//如果点在按钮上,让按钮成为选中状态
//获取当前手指所在的点
CGPoint curP = [self getCurPintWithTouches:touches];
//记录当前手指所在的点
self.curP = curP;
UIButton *btn = [self btnRectContainsPoint:curP];
if (btn && btn.selected == NO) {
//保存当前选中的按钮
[self.selectBtnArray addObject:btn];
btn.selected = YES;
}
//重绘
[self setNeedsDisplay];
}
//当手指松开时调用
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSMutableString *str = [NSMutableString string];
//取消所有选中的按钮
for (UIButton *btn in self.selectBtnArray) {
btn.selected = NO;
NSLog(@"%ld",btn.tag);
[str appendFormat:@"%ld",btn.tag];
}
NSLog(@"%@",str);
//清空数组
[self.selectBtnArray removeAllObjects];
//清空所有的连线
//重绘
[self setNeedsDisplay];
}
- (void)layoutSubviews {
[super layoutSubviews];
int column = 3;
CGFloat btnWH = 74;
CGFloat x = 0;
CGFloat y = 0;
CGFloat margin = (self.bounds.size.width - column * btnWH) / (column + 1);
int curL = 0;
int curR = 0;
for (int i = 0; i < self.subviews.count; i++) {
curL = i % column;
curR = i / column;
x = margin + (btnWH + margin) * curL;
y = margin + (btnWH + margin) * curR;
UIButton *btn = self.subviews[i];
btn.frame = CGRectMake(x, y, btnWH, btnWH);
}
}
- (void)drawRect:(CGRect)rect {
if (self.selectBtnArray.count) {
//创建路径
UIBezierPath *path = [UIBezierPath bezierPath];
//设置路径的起点
//遍历所有选中的按钮,如果是第一个按钮,按钮的中心成为路径的起点
for (int i = 0; i < self.selectBtnArray.count; i++) {
//取出选中的按钮
UIButton *btn = self.selectBtnArray[i];
//如果是第一个按钮,按钮的中心成为路径的起点
if (i == 0) {
[path moveToPoint:btn.center];
}else {
//不是第一个按钮, 添加一根线到按钮的中心
[path addLineToPoint:btn.center];
}
}
//添加一根线到当前手指所在的点
[path addLineToPoint:self.curP];
[path setLineJoinStyle:kCGLineJoinRound];
[path setLineWidth:10];
[[UIColor redColor] set];
//绘制路径
[path stroke];
}
}
@end
一个path路径可以描述多根线,但是只有一个状态,每次调用- (void)drawRect:(CGRect)rect
重绘的时候,都会清空之前所有的绘制的内容,也就是每次调用- (void)drawRect:(CGRect)rect
的时候,都是往空白的view上绘制内容
上面那个直接传size的方法,相当于下面那个方法,图片不透明,大小传的是0
UIImagePickerController现实代理方法后,需要自己去dismiss这个控制器,这个dismiss可以是当前的
控制器(自己)去dismiss,也可以是谁把它弹出来的,谁就可以去把它dismiss
画板相关代码-----
#import "ViewController.h"
#import "DrawView.h"
@interface ViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate>
@property (weak, nonatomic) IBOutlet DrawView *drawView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
//清屏
- (IBAction)clear:(id)sender {
[self.drawView clear];
}
//撤销
- (IBAction)undo:(id)sender {
[self.drawView undo];
}
//橡皮擦
- (IBAction)erase:(id)sender {
[self.drawView erase];
}
//照片
- (IBAction)photo:(id)sender {
//弹出系统相册,从中选择一张照片,把照片绘制到画板
UIImagePickerController *pickVC = [[UIImagePickerController alloc] init];
/**
UIImagePickerControllerSourceTypePhotoLibrary,
UIImagePickerControllerSourceTypeCamera,
UIImagePickerControllerSourceTypeSavedPhotosAlbum
*/
//设置照片的来源
pickVC.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
pickVC.delegate = self;
[self presentViewController:pickVC animated:YES completion:nil];
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
UIImage *image = info[UIImagePickerControllerOriginalImage];
NSData *data = UIImageJPEGRepresentation(image, 1);
[data writeToFile:@"/Users/xiaomage/Desktop/photo.jpg" atomically:YES];
[self dismissViewControllerAnimated:YES completion:nil];
self.drawView.image = image;
}
//保存
- (IBAction)save:(id)sender {
//对画板做截屏
UIGraphicsBeginImageContext(self.drawView.bounds.size);
//把View的layer的内容渲染到上下文当中
CGContextRef ctx = UIGraphicsGetCurrentContext();
[self.drawView.layer renderInContext:ctx];
//从上下文当中生成一张图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//关闭上下文
UIGraphicsEndImageContext();
//把生成的图片保存到系统相册
//保存完毕时调用的方法必须得是:image:didFinishSavingWithError:contextInfo:
UIImageWriteToSavedPhotosAlbum(newImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
NSLog(@"保存完毕");
}
//设置颜色
- (IBAction)setLineColor:(UIButton *)sender {
[self.drawView setLineColor:sender.backgroundColor];
}
//设置线宽度
- (IBAction)setLineWidth:(UISlider *)sender {
[self.drawView setLineWidth:sender.value];
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
@end
#import <UIKit/UIKit.h>
@interface DrawView : UIView
/**
* 清屏
*/
- (void)clear;
/**
* 撤销
*/
- (void)undo;
/**
* 橡皮擦
*/
- (void)erase;
/**
* 设置线的宽度
*
* @param width 指定的宽度
*/
- (void)setLineWidth:(CGFloat)width;
/**
* 置线的颜色
*
* @param color 指定的颜色
*/
- (void)setLineColor:(UIColor *)color;
/**
* 要绘制的图片
*/
@property (nonatomic ,strong) UIImage *image;
@end
#import "DrawView.h"
#import "MyBezierPath.h"
@interface DrawView ()
/**
* 当前绘制的路径
*/
@property (nonatomic, strong) UIBezierPath *path;
/**
* 保存当前绘制的所有路径 (一个路径只能对应一个状态)
*/
@property (nonatomic, strong) NSMutableArray *pathArray;
/**
* 当前绘制的线宽
*/
@property (nonatomic, assign) CGFloat width;
/**
* 当前绘制的线的颜色
*/
@property (nonatomic, strong) UIColor *color;
@end
@implementation DrawView
- (NSMutableArray *)pathArray {
if (_pathArray == nil) {
_pathArray = [NSMutableArray array];
}
return _pathArray;
}
- (void)awakeFromNib {
[super awakeFromNib];
//添加手势
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget: self action:@selector(pan:)];
[self addGestureRecognizer:pan];
self.width = 1;
self.color = [UIColor blackColor];
}
- (void)setImage:(UIImage *)image {
_image = image;
[self.pathArray addObject:image];
//重绘
[self setNeedsDisplay];
}
/**
* 清屏
*/
- (void)clear {
[self.pathArray removeAllObjects];
//重绘
[self setNeedsDisplay];
}
/**
* 撤销
*/
- (void)undo {
[self.pathArray removeLastObject];
//重绘
[self setNeedsDisplay];
}
/**
* 橡皮擦
*/
- (void)erase {
[self setLineColor:[UIColor whiteColor]];
}
/**
* 设置线的宽度
*
* @param width 指定的宽度
*/
- (void)setLineWidth:(CGFloat)width {
self.width = width;
}
/**
* 置线的颜色
*
* @param color 指定的颜色
*/
- (void)setLineColor:(UIColor *)color {
self.color = color;
}
- (void)pan:(UIPanGestureRecognizer *)pan {
//获取当前手指所在的点
CGPoint curP = [pan locationInView:self];
//画线
if (pan.state == UIGestureRecognizerStateBegan) {
//创建路径
MyBezierPath *path = [MyBezierPath bezierPath];
path.lineWidth = self.width;
path.lineJoinStyle = kCGLineJoinRound;
path.lineCapStyle = kCGLineCapRound;
//颜色必须得要在drawRect方法当中进行绘制
path.lineColor = self.color;
//当发现系统的类,没有办法满足我们要求时,继承系统类,添加属性我们自己的东西.
self.path = path;
//设置路径的起点
[self.path moveToPoint:curP];
//保存路径
[self.pathArray addObject:path];
}else if (pan.state == UIGestureRecognizerStateChanged) {
//添加一根线到当前手指所在的点
[self.path addLineToPoint:curP];
//重绘
[self setNeedsDisplay];
}
}
- (void)drawRect:(CGRect)rect {
//绘制所有的路径
for (MyBezierPath *path in self.pathArray) {
if ([path isKindOfClass:[UIImage class]]) {
UIImage *image = (UIImage *)path;
[image drawInRect:rect];
}else {
[path.lineColor set];
[path stroke];
}
}
}
@end
#import <UIKit/UIKit.h>
@interface MyBezierPath : UIBezierPath
/**
* 当前路径的颜色
*/
@property (nonatomic, strong) UIColor *lineColor;
@end