九宫格解锁的实现

先来看一下效果图

我们用一个数组来记录下目标点(即图中的九个点)的view,再在每次的touchesMoved方法中对这个数组遍历,在各个点之间画线.主窗体上覆盖一个UIImageView控件来显示画出的线

.h文件

 1 #import <UIKit/UIKit.h>
 2 
 3 @interface VcLock : UIViewController{
 4     
 5     UIImageView * imgV;
 6     
 7     NSMutableArray * arrTag;//记录被选中按钮的tag值
 8 }
 9 
10 @property(strong,nonatomic)IBOutlet UIImageView * imgV;//从上下文对象获取绘制的图形
11 @property(strong,nonatomic) NSMutableArray * arrTag;
12 
13 -(UIImage *)drawLine:(NSArray *)_arrPoint tempPoint:(NSValue *)tempPointValue;
14 @end

.h的实现文件,首先定义一个宏,表明共有三行三列

#define max_number 3

在viewDidLoad方法中对界面进行初始化,需要注意的是ios中所有控件的tag默认为0,且为NSInteger类型,这样我们就可以通过tag值来区分目标点和用于显示连线的UIImageView控件.将九个目标点的tag从左到右自上而下依次赋为1-9,区别于最后用于显示连线的tag=0的UIImageView控件.我们并未将九个目标点设为全局变量,这样可以通过tag值查找当前view的subview并将其强制转换为UIImageView

 1 - (void)viewDidLoad
 2 {
 3     [super viewDidLoad];
 4     int singleWidth=self.view.frame.size.width/max_number;
 5     int singleHeight=self.view.frame.size.height/max_number;
 6     for (int i=0; i<max_number; i++) {
 7         for (int j=0; j<max_number; j++) {
 8             UIImage *dotImage = [UIImage imageNamed:@"dot_off.png"];
 9             UIImageView * v=[[UIImageView alloc]initWithImage:dotImage highlightedImage:[UIImage imageNamed:@"dot_on.png"]];
10             v.backgroundColor=[UIColor clearColor];
11             v.frame=CGRectMake(0, 0, dotImage.size.width, dotImage.size.height);
12             int x=singleWidth/2+singleWidth*j;
13             int y=singleHeight/2+singleHeight*i;
14             v.center=CGPointMake(x, y);
15             v.tag=i*max_number+j+1;//tag值为1-9,ios所有控件默认tag值为0
16             v.userInteractionEnabled= YES;
17             [self.view addSubview:v];
18         }
19     }
20     self.imgV=[[UIImageView alloc]initWithFrame:self.view.bounds];
21     [self.view addSubview:self.imgV];
22 }

接着要看到的便是核心代码了,对记录选中目标点之间进行连线的代码,涉及肤浅的Core Graphics框架知识

-(UIImage *)drawLine:(NSArray *)_arrPoint tempPoint:(NSValue *)tempPointValue{
    if (!tempPointValue) {//当数组未存储触摸到的点时
        return NULL;
    }
    UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);//创建一个适用于图形操作的当前上下文
    CGContextRef context_=UIGraphicsGetCurrentContext();//获取该上下文

    CGContextSetLineWidth(context_, 5.0);//设置画笔线条粗细
    CGContextSetLineCap(context_, kCGLineCapButt);//设置线条样式
    CGContextSetRGBStrokeColor(context_, 1, 0, 0, 1);//设置画笔颜色:黑色
    CGContextSetAlpha(context_, 1.0);
    
    //递归画线
    CGPoint fromPoint;
    UIView * lastPointVIew;
    for (UIView * v in _arrPoint) {
        fromPoint=v.center;
        if (!lastPointVIew) {//lastPointVIew为空
            CGContextMoveToPoint(context_, fromPoint.x, fromPoint.y);//指定直线的初始点
        } else {
            CGContextAddLineToPoint(context_, fromPoint.x, fromPoint.y);

        }
        lastPointVIew=v;
    }
    
    CGPoint tempPoint=[tempPointValue CGPointValue];//获取临时点的坐标
    CGContextAddLineToPoint(context_, tempPoint.x, tempPoint.y);//在最后一个触摸点到当前触摸位置之间画线
    CGContextStrokePath(context_);
    UIImage * imagePoint=UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return imagePoint;
}

关于此自定义方法的两个参数_arrPoint和tempPointValue,第一个参数便是头文件中定义的数组,用于保存被触摸到的目标点地view,保留view类型的参数不仅能保留目标点的tag属性,而且还能保留目标点的center属性,用于在各个目标点之间连线,递归画线即对数组中保存的目标点的center信息进行遍历,不断在各个center之间连线.tempPointValue参数是用于记录在手指移动过程中触摸到的一般位置(包括非首次触摸到的目标点,即已标记高亮的目标点),递归画线将数组中最后一个view的center作为起点,将手指划过的一般点作为终点再次连线,此方法每次返回一个UIImage类型的变量作为覆盖view的UIImageView的image属性.

手指触摸过程中的处理,主要是判断何时向数组中添加目标点

touchesBegan方法,值得注意的是我们需要在此方法中对存储目标点的数组进行初始化,且初始化后的数组便不再为null.通过tag值来判断触摸点是否为目标点

 1 -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
 2     NSLog(@"begin");
 3     self.arrTag=[[NSMutableArray alloc]initWithCapacity:9];//此处数组必须初始化,否则无法添加元素
 4     CGPoint point=[[touches anyObject] locationInView:self.view];
 5     UIImageView * touchedView=(UIImageView * )[self.view hitTest:point withEvent:event];
 6     if (touchedView.tag!=0) {
 7         [self.arrTag addObject:touchedView];
 8         touchedView.highlighted=YES;
 9     }
10 }

touchesMoved方法,每一次都对UIImageView重新赋予绘制的image

 1 -(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
 2     NSLog(@"moved");
 3     CGPoint point=[[touches anyObject]locationInView:self.view];
 4     NSValue * pointValue=nil;
 5     UIImageView * touchedView=(UIImageView *)[self.view hitTest:point withEvent:event];
 6     if (touchedView.tag!=0&&touchedView.highlighted==NO) {//当任一圆被第一次触摸
 7             [self.arrTag addObject:touchedView];
 8             touchedView.highlighted=YES;
 9     }else{//触摸点不为第一次触摸的圆
10         pointValue=[NSValue valueWithCGPoint:point];
11     }
12     UIImage * pointImage=[self drawLine:self.arrTag tempPoint:pointValue];
13     self.imgV.image=pointImage;
14 }

touchesEnded方法,用于将所有被选中的目标点进行取消高亮显示并获取其tag值,取出的tag值可用于拼接为一个数组保存在本地,这样以后每次启动程序时便核对拼接成的字符串与本地的保存字符串是否相等来判断手势正确性

-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    self.imgV.image=nil;
    for (UIImageView * imageView in self.arrTag) {
        imageView.highlighted=NO;
        NSLog(@"%d\n",imageView.tag);
    }
    self.arrTag=nil;//此处必须清空数组,避免第二次开始触摸事件时未触摸的点高亮
}

 附上源代码

原文地址:https://www.cnblogs.com/candr/p/3224033.html