iOS手勢解鎖和畫板

給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
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容