給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