

























最近抽空写了个拼图验证控件,用户可以通过水平、垂直,或者直接拖动拼图块,完成拼图图案,来完成验证。拼图块的形状可以自定义,默认提供了经典的拼图形状、圆形、正方形,整个拼图的图案也是支持从图片生成的。
CocoaPods: pod "TTGPuzzleVerify"
Github: https://github.com/zekunyan/TTGPuzzleVerify

接下来,说说TTGPuzzleVerify的整体设计思路和实现原理。
先直观上分析一下控件的组成部分:

可以直观看出,控件应该由三个部分组成:
分析完了控件的组成,现在就来梳理一下控件的整个创建、交互流程。

从上面的步骤可以整理出来几个关键的技术点,如下:
拼图的路径生成
普通的形状,如正方形、圆形之类的还好,代码实现比较方便,但是复杂的拼图图案怎么办?
从图片上“抠出”拼图块、镂空
有了拼图的形状路径,如何从图片上抠出拼图块和镂空?
用户拖动拼图、完成拼图验证
抠出了拼图块,如何将用户的手势对应到移动拼图块,最终完成验证?
下面针对这三个点着重写下实现原理。
既然手写代码太难,那就从现有的图案生成代码。
1. 找SVG素材
既然要能随意改变大小,就要找矢量的图,Google “free svg”、“free icon”之类的,很容易就能找到免费的矢量图素材,例如TTGPuzzleVerify里面用的拼图形状就是从iconmonstr.com找来的。
2. 编辑
直接下载回来的SVG还需要调整一下,如去除背景,我是用Sketch来编辑的。

3. 生成代码
拼图的路径本质上就是拼图的轮廓,在Sketch里面把拼图SVG的背景色等颜色删除,然后就是将其转换为代码。
这里可以用PaintCode plugin for Sketch,将Sketch的拼图矢量路径导出成代码。
4. 调整代码
直接生成的代码有时可能会不适用,可以人工调整下,然后就可以放到自己的类里面了:
1 | + (UIBezierPath *)classicPuzzlePath { |
5. UIBezierPath拼图路径的大小、位置调整
直接导出的拼图路径的大小、位置是固定的,但是控件是要可以支持改变大小、位置的,所以,要对路径做变换,刚好UIBezierPath支持CGAffineTransform变换。
大小变换:
1 |
|
位置变换:
1 | // 位置变换,path是生成的原始路径,_puzzleBlankPosition是传入的拼图块、镂空位置 |
至此,拼图的路径就写好了。
1. 拼图块
有了UIBezierPath路径,抠出拼图块还是比较容易的,直接用CAShapeLayer来“mask”图片即可:
1 |
|
效果就是:

2. 镂空图片
抠出拼图块还是很容易的,但是“镂空”怎么办?
其实道理是一样的,都是mask,只不过mask的区域反过来而已。CAShapeLayer的fillRule就可以调整“填充的规则”,此处用kCAFillRuleEvenOdd就可以了,文档的解释如下:
Specifies the even-odd winding rule. Count the total number of path crossings. If the number of crossings is even, the point is outside the path. If the number of crossings is odd, the point is inside the path and the region containing it should be filled.
理解过来就是:区域内一点,往外划一条线,经过奇数个数的交点,则点在区域内,偶数个数的交点,则在区域外。
所以,只要在拼图路径的基础上,补充最外部的边框的路径即可:
1 |
|
效果:

3. 镂空背景
有了拼图块、镂空,还差一块,就是镂空的“半透明背景”,其实这个“半透明背景”和拼图块是一模一样的,所以不再重复代码,唯一增加的就是设置其透明度。
4. 组合
将上面的组合到一起,就能形成最终的效果,从Reveal里面看到分层结构如下:

其实就是三张一样的图片,按照不同的mask规则做了处理,叠加起来。
1. 拖动图片
有了上面的结构,拖动拼图的处理也就容易多了,只要移动最上层的拼图块就行了,这里直接用UIPanGestureRecognizer实现,移动的时候基于拼图的中心移动。
1 |
|
2. 验证用户的拼图是否完成
用户移动拼图时,只要判断拼图的坐标和镂空的位置是否一致就行。但是如果完全精确的判断,用户很难准确的把拼图拼对上,所以还要增加一定的“tolerance”,让拼图更加容易,同时暴漏出让外部设置:
1 | @property (nonatomic, assign) CGFloat verificationTolerance; |
判断的逻辑如下:
1 |
|
3. 完成拼图
当用户完成拼图后,外部会得到delegate、block的回调响应,接着就可以“完成拼图验证”,对此向外提供一个方法:- (void)completeVerificationWithAnimation:(BOOL)withAnimation,调用后拼图块将会移动回镂空位置,模拟出拼图完成的效果。
至此,TTGPuzzleVerify的关键实现技术点就是这些了~
嗯,圣诞节快乐! – 2016-12-25
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。