前几天接到一个需求,在SDK中加入一个画圈的手势,第三方在接入后可通过这个手势来触发某个事件,而且这个手势在任何界面都可以触发.当时第一感觉就是想睡会...
完成需求就需要解决以下几个问题:
Q1:如何在全屏任何界面触发,并在触发手势的时候,不影响UIScrollView及其子类的响应.
Q2:如何识别用户画的是一个圈
Q1:如何识别全屏手势
1.第一想法就是直接重写UIWindow使用-(void)touchesBegan:(NSSet
获取全屏点击坐标,但可能会影响到第三方应用(方案pass,此方案也会出现手势冲突问题)
2.最终解决方案就是创建一个NSObject类,获取当前keyWindow,然后在keyWindow上增加一个pan手势,通过pan手势事件获取CGPoint.
CGPoint touchPoint = [recognizer translationInView:[UIApplication sharedApplication].keyWindow];
switch (recognizer.state) {
case UIGestureRecognizerStateBegan://手势开始
break;
case UIGestureRecognizerStateChanged://移动
break;
case UIGestureRecognizerStateEnded://停止
break;
case UIGestureRecognizerStateCancelled://取消
break;
case UIGestureRecognizerStateFailed://失败
break;
default:
break;
}
代码完成,美美的command R.在页面完美的描绘出各个point,返回首页的时候,遇到了问题,UITableView滑动的时候我完美的point消失了.为啥?凭啥!拥护啥?
查阅了很多资料,我的理解就是当手势滑动到UIScrollView时,第一响应者会被其拦截,来响应UIScrollView的滚动事件(个人理解).最终找到使用手势的代理方法解决此问题.
//手势共存支持
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Q2:如何识别用户画的是一个圈
解决完手势问题后,就可以收集一堆数据,拿着这一把数据源又陷入沉思.应该如何处理来认定用户画的是一个圆?怎么求圆心?
突然想到中学时候学到求圆心的方法,把相邻的两个点分为一组,然后通过这两点划直线.再让下一组两点划直线...画出的所有直线再取垂线,所有垂线的焦点就是圆心(好像是寒假作业上一个求破碎镜子的题.....TMD暴露年龄了).这个算法至少需要4次循环,而且计算出的所有垂线焦点也是各处都有,果断放弃默默的拿起了高数书...
找到一个"最小二乘法".
最小二乘法(又称最小平方法)是一种数学优化技术。它通过最小化误差....(自行百度去吧皮卡丘~后面会贴代码).
最终计算步骤:
1.把points里的x和y分别取出来,然后去x和y的平均数
2.用最小二乘法计算圆心点
3.取各个点到圆心的距离,取平均数作为平均半径
4.设定一个置信区间(就是你能容忍大于或者小于平均半径的范围)
5.当置信区间大于95%的时候,就可以认为是一个圆形
以下是实现代码:
//
// GestureRecognizer.h
//
// Created by Andrew
// Copyright © 2017年. All rights reserved.
//
#import "GestureRecognizer.h"
#import
#define reasonable_distance 40 //可容忍范围
#define reasonable_finger_distance 200
@interface GestureRecognizer()
{
NSMutableArray *array;
UIPanGestureRecognizer *pan;
}
@property (nonatomic, copy) EnableGestureSuccessBlock enableGestureSuccessBlock;
@end
@implementation GestureRecognizer
static GestureRecognizer *_instance;
+ (instancetype)sharedGestureRecognizer
{
static dispatch_once_t onceToken_GestureRecognizer;
dispatch_once(&onceToken_GestureRecognizer, ^{
_instance = [[self alloc]init];
});
return _instance;
}
- (void)startGestureRecognitionWithEnableGestureBlock:(EnableGestureSuccessBlock)enableGesture
{
_enableGestureSuccessBlock = enableGesture;
pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(getCoordinates:)];
pan.delegate = self;
[[UIApplication sharedApplication].keyWindow addGestureRecognizer:pan];
}
- (void)stopGestureRecognition
{
[[UIApplication sharedApplication].keyWindow removeGestureRecognizer:pan];
}
- (void)resumeGestureRecogniton
{
[[UIApplication sharedApplication].keyWindow addGestureRecognizer:pan];
}
//手势共存支持
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (void)getCoordinates:(UIPanGestureRecognizer *)recognizer
{
CGPoint touchPoint = [recognizer translationInView:[UIApplication sharedApplication].keyWindow];
switch (recognizer.state) {
case UIGestureRecognizerStateBegan://手势开始
array = [NSMutableArray array];
break;
case UIGestureRecognizerStateChanged://移动
//记录每一个移动坐标
[array addObject:@[[NSString stringWithFormat:@"%.f",touchPoint.x],[NSString stringWithFormat:@"%.f",touchPoint.y]]];
break;
case UIGestureRecognizerStateEnded://停止
//结束手势,计算圆心
if (array.count > 10) {//数据大于10,计算才有意义
[self getCircleCenter:array];
} else {
TILog(@"手势识别数据量小于10组");
}
break;
case UIGestureRecognizerStateCancelled://取消
break;
case UIGestureRecognizerStateFailed://失败
break;
default:
break;
}
}
#pragma mark - 计算圆心方法
- (void)getCircleCenter:(NSMutableArray *)dataSource
{
NSMutableArray __block *xArr = [NSMutableArray array];
NSMutableArray __block *yArr = [NSMutableArray array];
double __block max_X, min_X, max_Y, min_Y = 0.0;
//分别取 x 和 y坐标的集合
[dataSource enumerateObjectsUsingBlock:^(NSArray *obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj.count >= 2) {
double kx = [[obj objectAtIndex:0] doubleValue];
double ky = [[obj objectAtIndex:1] doubleValue];
if (kx > max_X) { max_X = kx; }
if (kx < min_X) { min_X = kx; }
if (ky > max_Y) { max_Y = ky; }
if (ky < min_Y) { min_Y = ky; }
[xArr addObject:[obj objectAtIndex:0]];
[yArr addObject:[obj objectAtIndex:1]];
}
}];
//去平均数
double _x = [[xArr valueForKeyPath:@"@avg.floatValue"] doubleValue];
double _y = [[yArr valueForKeyPath:@"@avg.floatValue"] doubleValue];
double __block Suuu, Svvv, Suu, Svv, Suv, Suuv, Suvv = 0.0;
//计算圆心
[dataSource enumerateObjectsUsingBlock:^(NSArray *obj, NSUInteger idx, BOOL * _Nonnull stop) {
double Ui = [[obj objectAtIndex:0] doubleValue] - _x;
double Vi = [[obj objectAtIndex:1] doubleValue] - _y;
Suuu = Suuu + pow(Ui, 3);
Svvv = Svvv + pow(Vi, 3);
Suu = Suu + pow(Ui, 2);
Svv = Svv + pow(Vi, 2);
Suv = Suv + Ui * Vi;
Suuv = Suuv + pow(Ui, 2) * Vi;
Suvv = Suvv + pow(Vi, 2) * Ui;
}];
//圆心x值
double Xc = (Suuv * Suv - Suuu * Svv - Suvv * Svv + Suv * Svvv) / ((pow(Suv, 2) - Suu * Svv) * 2) + _x;
//圆心y值
double Yc = (Suuu * Suv - Suu * Suuv - Suu * Svvv + Suv * Suvv) / ((pow(Suv, 2) - Suu * Svv) * 2) + _y;
if (!isnan(Xc) && !isnan(Yc)
&& Xc > - reasonable_distance && Xc < SCREEN_WIDTH //圆点x坐标在屏幕范围之内
&& Yc > - reasonable_distance && Yc < SCREEN_HEIGHT //圆点y坐标在屏幕范围之内
&& Xc < max_X
&& Xc > min_X
&& Yc > min_Y
&& Yc < max_Y) {
[self determineCirclePointX:Xc withPointY:Yc withDataSource:dataSource];
} else {
if (_enableGestureSuccessBlock) {
_enableGestureSuccessBlock(NO);
}
}
}
//判定是否是圆形
- (void)determineCirclePointX:(double)point_x withPointY:(double)point_y withDataSource:(NSMutableArray *)dataSource
{
NSMutableArray __block *distanceArr = [NSMutableArray array];
//计算每个点到圆心的距离
[dataSource enumerateObjectsUsingBlock:^(NSArray *obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj.count >= 2) {
double x = [[obj objectAtIndex:0] doubleValue];
double y = [[obj objectAtIndex:1] doubleValue];
double distance = [self distanceFromPointX:CGPointMake(point_x, point_y) distanceToPointY:CGPointMake(x, y)];
[distanceArr addObject:[NSString stringWithFormat:@"%.f",distance]];
}
}];
//取平均距离
double avgDistance = [[distanceArr valueForKeyPath:@"@avg.floatValue"] doubleValue];
//计算置信值 >95%可认为是圆
int __block count = 0;
[distanceArr enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (fabs([obj doubleValue] - avgDistance) <= reasonable_distance) {
count ++;
}
}];
if (count / distanceArr.count > 0.80) {
if (_enableGestureSuccessBlock) {
//震动提醒
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
//声音提醒
AudioServicesPlaySystemSound(1109);
_enableGestureSuccessBlock(YES);
}
} else {
if (_enableGestureSuccessBlock) {
_enableGestureSuccessBlock(NO);
}
}
}
//计算两点之间距离
- (float)distanceFromPointX:(CGPoint)start distanceToPointY:(CGPoint)end
{
float distance;
CGFloat xDist = (end.x - start.x);
CGFloat yDist = (end.y - start.y);
distance = sqrt((xDist * xDist) + (yDist * yDist));
return distance;
}
@end
只是一个随笔,希望给一些人提供一些思路.如果有什么不对的地方,望指正.