利用触摸功能可以与drawRect可以实现画板。
由于数组中只能存对象,所以不能用CGMutablePathRef来存储路径,使用UIBezierPath即可。UIBezierPath相当于CGMutablePathRef的OC版本。
原理:
将触碰的每一个点存入数组,在drawRect中根据数组全部重新绘制一遍。
所以清屏只要移除数组中的所有元素即可,回退则删除数组最后一个元素。
主体代码:
// 开始触摸
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 1.获取手指对应UITouch对象
UITouch *touch = [touches anyObject];
// 2.通过UITouch对象获取手指触摸的位置
CGPoint startPoint = [touch locationInView:touch.view];
// 3.当用户手指按下的时候创建一条路径
UIBezierPath *path = [UIBezierPath bezierPath];
// 3.1设置路径的相关属性
[path setLineJoinStyle:kCGLineJoinRound];
[path setLineCapStyle:kCGLineCapRound];
[path setLineWidth:10];
// 4.设置当前路径的起点
[path moveToPoint:startPoint];
// 5.将路径添加到数组中
[self.paths addObject:path];
}
// 移动
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// 1.获取手指对应UITouch对象
UITouch *touch = [touches anyObject];
// 2.通过UITouch对象获取手指触摸的位置
CGPoint movePoint = [touch locationInView:touch.view];
// 3.取出当前的path
UIBezierPath *currentPaht = [self.paths lastObject];
// 4.设置当前路径的终点
[currentPaht addLineToPoint:movePoint];
// 6.调用drawRect方法重回视图
[self setNeedsDisplay];
}
// 离开view(停止触摸)
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
}
// 画线
- (void)drawRect:(CGRect)rect
{
[[UIColor redColor] set];
// 边路数组绘制所有的线段
for (UIBezierPath *path in self.paths) {
[path stroke];
}
}
原理:
根据接触点是否在按键的frame内判断按键选中状态,并记录当前接触点
根据按键的选中状态,将按键添加入数组
连接数组中按键的中点,然后如果按键数组不为空则添加与当前接触点的连线
离开屏幕时清除数组以去除连线,重置选中状态
主要代码:
@property (nonatomic, strong) NSMutableArray *buttons;
/** * 定义属性,记录用户当前手指的位置(非按钮范围内) */
@property (nonatomic, assign) CGPoint currentPoint;
- (NSMutableArray *)buttons
{
if (nil == _buttons) {
_buttons = [NSMutableArray array];
}
return _buttons;
}
- (void)setup
{
for (int i = 0; i < 9; i++) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
// 禁止按钮的点击事件(因为我们需要监听触摸事件)
button.userInteractionEnabled = NO;
[self addSubview:button];
}
}
- (void)layoutSubviews
{
UIImage *imageNormal = [UIImage imageNamed:@"gesture_node_normal"];
UIImage *imageSelected = [UIImage imageNamed:@"gesture_node_highlighted"];
float gapX = (self.frame.size.width - 3 * imageNormal.size.width) / 4.0;
float gapY = gapX;
CGSize buttonSize = imageNormal.size;
for (int i = 0; i < 9; i++) {
UIButton *button = self.subviews[i];
[button setBackgroundImage:imageNormal forState:UIControlStateNormal];
[button setBackgroundImage:imageSelected forState:UIControlStateSelected];
float buttonX = gapX + i % 3 * (buttonSize.width + gapX);
float buttonY = gapY + i / 3 * (buttonSize.height + gapY);
button.frame = CGRectMake(buttonX, buttonY, buttonSize.width, buttonSize.height);
}
}
// 通过xib或者storyboard加载时调用
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
[self setup];
self.backgroundColor = [UIColor clearColor];
}
return self;
}
// 通过代码创建时调用
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setup];
self.backgroundColor = [UIColor clearColor];
}
return self;
}
/** * 根据触摸点获取触摸到的按钮 * @return 触摸的按钮 */
- (UIButton *)getCurrentBtnWithPoint:(CGPoint)point
{
// 判断触摸的位置是否在按钮的范围内
for (UIButton *btn in self.subviews) {
// rect是否包含point
if (CGRectContainsPoint(btn.frame, point)) {
return btn;
}
}
return nil;
}
/** * 根据系统传入的UITouch集合获取当前触摸的点 * @return 当初触摸的点 */
- (CGPoint)getCurrentTouchPoint:(NSSet *)touches
{
// 获取按下的点
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:touch.view];
return point;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 1.获取按下的点
CGPoint startPoint = [self getCurrentTouchPoint:touches];
// 2.判断触摸的位置是否在按钮的范围内
UIButton *btn = [self getCurrentBtnWithPoint:startPoint];
// 存储按钮
if (btn)
{
// 设置选中状态
btn.selected = YES;
// 将按钮保存到数组中
[self.buttons addObject:btn];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// 1.获取按下的点
CGPoint movePoint = [self getCurrentTouchPoint:touches];
// 2.获取触摸的按钮
UIButton *btn = [self getCurrentBtnWithPoint:movePoint];
// 存储按钮
if (btn && btn.selected != YES)
{
// 设置选中状态
btn.selected = YES;
// 将按钮保存到数组中
[self.buttons addObject:btn];
}
// 记录当前手指移动的位置
self.currentPoint = movePoint;
// 通知view绘制线段
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// 遍历数组并对其中的每个元素调用setSelected:方法
// 去除选中状态
[self.buttons makeObjectsPerformSelector:@selector(setSelected:) withObject:@(NO)];
// 清空数组,清楚连线
[self.buttons removeAllObjects];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
CGContextRef ctf = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctf, 15.0);
CGContextSetLineCap(ctf, kCGLineCapRound);
CGContextSetLineJoin(ctf, kCGLineJoinRound);
[[UIColor colorWithRed:10 / 255.0 green:100 / 255.0 blue:50 / 255.0 alpha:0.5] set];
for (int i = 0; i < self.buttons.count; i++) {
CGPoint center = [self.buttons[i] center];
if (0 == i) {
CGContextMoveToPoint(ctf, center.x, center.y);
} else {
CGContextAddLineToPoint(ctf, center.x, center.y);
}
}
// 不能用判断这个点是不是0,0来决定,即使end那里清零这个点,在进行快速点击时,还是会发生错误,坐标会出现不为0的情况。
// if (!CGPointEqualToPoint(CGPointZero, self.currentPoint)) {
// CGContextAddLineToPoint(ctf, self.currentPoint.x, self.currentPoint.y);
// }
// 判断数组中是否有按钮, 如果有按钮就有起点, 有起点就不会报错
if (self.buttons.count) {
CGContextAddLineToPoint(ctf, self.currentPoint.x, self.currentPoint.y);
}
CGContextStrokePath(ctf);
}
需要输出密码时,可设置button的tag,并设置代理,在end时将密码传出,由控制器进行判断密码是否正确等操作