iOS全屏手势

前几天接到一个需求,在SDK中加入一个画圈的手势,第三方在接入后可通过这个手势来触发某个事件,而且这个手势在任何界面都可以触发.当时第一感觉就是想睡会...

完成需求就需要解决以下几个问题:
Q1:如何在全屏任何界面触发,并在触发手势的时候,不影响UIScrollView及其子类的响应.
Q2:如何识别用户画的是一个圈

Q1:如何识别全屏手势

1.第一想法就是直接重写UIWindow使用-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event;获取全屏点击坐标,但可能会影响到第三方应用(方案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;
}

<br />

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 <AVFoundation/AVFoundation.h>

#define reasonable_distance 40  //可容忍范围
#define reasonable_finger_distance 200

@interface GestureRecognizer() <UIGestureRecognizerDelegate>
{
    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

只是一个随笔,希望给一些人提供一些思路.如果有什么不对的地方,望指正.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • 目的:当push到下一控制器时,里面整个界面用手势侧滑是可以返回的步骤:1.自己写一个UINavigationCo...
    觞咏畅情阅读 463评论 0 0
  • 手势识别器是附加到视图的对象,将低级别事件处理代码转换为更高级别的操作,它允许视图以控件执行的方式响应操作。 手势...
    坤坤同学阅读 4,073评论 0 9
  • 不知不觉中,我长大了还记得小时候,您从来没抱过我一次,亲过我一次~我从来都不敢与您亲近,见了甚至都想哭~还记得小时...
    瀛洲惠子阅读 385评论 0 0
  • 到此为止吧!他冷冷的说。 她低头,凝视桌上的咖啡,右手不断搅动杯中的银匙。白色泡沫与褐色液体混在一起,一圈圈映出靶...
    自菲薄阅读 89评论 0 0