iOS全屏手势

作者: 我就叫Tom怎么了 | 来源:发表于2017-03-02 18:58 被阅读0次

    前几天接到一个需求,在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
    
    

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

    相关文章

      网友评论

        本文标题:iOS全屏手势

        本文链接:https://www.haomeiwen.com/subject/lxrkgttx.html