代码库> 手势交互> IOS单手“旋转”手势识别
IOS单手“旋转”手势识别
关注
上传者:小哈加尔 分类:手势交互(Gesture)
查看次数:15353 下载次数:621
上传时间:2012-11-07 大小:54 B
【原创】转载请表明原帖连接

亲们啊。码子真不容易啊。。。。。TT

有发帖子了。这几天真是很“闲”啊,研究了一下手势识别。这次发的主要是用于“懒人”,单手“旋转”手势。
就是用一只手画圈圈的识别,理论上呢,是可以识别旋转方向,和旋转角度的。但是因为本人比较菜,算
角度的时候有点小问题,这个角度计算有时候不准确,但是基本不影响手势的识别。
-----------
说下为什么旋转要用“旋转呢”,因为旋转只是其中可以使用这个手势的地方。当然,如果你有更好的创意,
可以用在很多地方哈。(在角度计算bug修复后。。)

代码中用到了我第二个帖子中的CircleCore,嗯。。。做了稍微的小修改,就是分离了一个函数
第二个帖子代码链接如下:
http://www.cocoachina.com/bbs/read.php?tid=124841
第一个帖子是不规则UIButton实现,连接如下:
http://www.cocoachina.com/bbs/read.php?tid=124406
我不会告诉你我在给自己的帖子做广告

说正事吧。。。。
算法主要用到了高中数学的几何计算,一元二次方程求解,当然,本人数学“”非常好“”,而且很“”喜欢“”数学,所以大学期间,一门数学我就学了两次哈哈(你懂得)
先说下LargeCircle是外圆,smallCircle是内圆,两个圆是同心圆,当手指移出大圆范围时,手势不识别,理论上当手指移如小圆时候同样不识别,但是我发现不做
小圆判断的时候效果也不错,所以在程序中没有判断小圆识别。
基本思想就是,在手指移动的开始,取得一点tangentLine_start 在移动的瞬间取得长度位tangentLine_Length的线段,终点为tangentLine_end
这样就得到了一条线段lin1
以为直线可以用 y = kx+b表示。
所以定义一个结构体来表示 直线


typedef struct {
float k;
float b;
}MathLineFuncton;


然后找到这条线断的中点midPoint
然后求得直线line2,满足:line2垂直line1并且过midpoint
line2非常重要哈,因为我们的两个判定圆圆心就在这条直线上,并且距离这条直线的距离为M
在程序中M被设定为大圆的Radius * 0.6
因为这样得到的效果较好,

也许你会问:为什么是两个判定圆,实际上我们只是需要单手绕着一个圆
的确,但是仅仅通过一个切线是不能判断用户手指即将运动的方向的即逆时针 Or 顺时针,
所以也就无法判定具体使用哪个圆。而且距离一条直线上一点M距离的点在直线上有两个点,也就是有两个圆心


不用担心,我们还有进一步判定
当手指继续移动时,会计算 当前点于两个圆心的距离
如果某一个距离大于 大圆的Radius * weight//weight为权重 在程序中设定为0.6
就放弃那个判定圆,

这个时候这个判定圆已经确定。
最重要的一步已经完成。
接下来就是确定什么时候识别这个手势
在程序中我是用的方法是角度判断。
通过移动转过的的角度(相对手指触摸的起点即 tangentLine_start)
与minThresholdDegree_比较大小来实现。
minThresholdDegree_是自己设定的最小识别角度。在程序中被设定为200度。(我觉得这样比较舒服)
基本上就这些,当然还有判断顺时针和逆时针,这个比较简单了。就不说了

在这个例子中会有些bug。因为本人菜鸟吗,,也没弄得那么详细。。就是个人兴趣所以做了一个demo。如果有时间会完善下的。
高手看到勿喷啊。
PS:如果你对代码有什么更好的意见或者建议,或者优化以及修改,欢迎留言 或者给我发邮件
如果你下载代码,会发现其中包含一些别的内容,没错,是demo,通过这个demo,你可以直接看到手势识别的结果,当然还有“转过度数统计的BUG”哈哈

good good study day day up!
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------

//
// CircleRecognizer.h
// five_min_TEST
//
// Created by 仙人掌 on 12-11-6.
// Copyright (c) 2012年 仙人掌. All rights reserved.
//

#import
#import

#define KDegreeKey @"degree_key"
#define KWiseTypeKey @"wise_type_key"
#define kRecognizeCircleNotification @"recognizeCirclenotification"
@class CircleCore;
typedef enum{
WT_NONE = 0,
WT_ANTI_CLOCK_WISE,
WT_CLOCK_WISE,
}kWISE_TYPE;

@interface CircleRecognizer : UIGestureRecognizer{

CircleCore *circleCore_;
kWISE_TYPE wisetype;
CGPoint center1_;
CGPoint center2_;
CGPoint tangentLineStart_;
CGPoint tangentLineEnd_;
float tangentLineLength_;
float minThresholdDegree_;
float lastDegree_;
float zeroDegree_;
float sumdeg_;
float weight_;//权重
float weight2_;
BOOL bCircleConfirm_;
BOOL bTangentLineConfirm_;
BOOL bDirectionConfirm_;
BOOL bZeroDegConfirm_;

}
@end

//----------------------------------------------------------------------------------------------------
//
// CircleRecognizer.m
// five_min_TEST
//
// Created by 仙人掌 on 12-11-6.
// Copyright (c) 2012年 仙人掌. All rights reserved.
//

#import "CircleRecognizer.h"
#import "CircleCore.h"
//已知line y = kx +b 过point1 与point2点
//可求k, b
typedef struct {
float k;
float b;
}MathLineFuncton;
MathLineFuncton GetMathLineFunctionPPoint( CGPoint point1, CGPoint point2);
MathLineFuncton GetMathLineFunctionPPoint( CGPoint point1, CGPoint point2){
MathLineFuncton fun;
fun.k = ( point1.y - point2.y ) / ( point1.x - point2.x );
fun.b = point1.y - fun.k * point1.x;
return fun;
}

CGPoint GetMidPoint( CGPoint point1, CGPoint point2 );
CGPoint GetMidPoint( CGPoint point1, CGPoint point2 ){
return CGPointMake( ( point1.x + point2.x ) / 2, ( point2.y + point2.y ) /2 );
}
MathLineFuncton GetMathLineFunctionKPoint ( float k, CGPoint point);
//GetMathLineFunctionKPoint
//是求与斜率为k且过point点的直线的垂直直线,并且页过point点
MathLineFuncton GetMathLineFunctionKPoint ( float k, CGPoint point){
MathLineFuncton fun;
// 如果给的k为0,那么于这条直线垂直的直线不存在斜率
// 但是在程序中这样处理会很费劲,所以这里指定一个非常大的斜率
// 这个516.0就是这个斜率。没有什么特别意思
if ( k - 0.0f > -0.0001 && k - 0.0f < 0.0001 )
fun.k = 516.0f;
else
fun.k = -( 1 / k );
fun.b = point.y - fun.k * point.x;
return fun;
}
void GetPointOnMathLineFunction ( MathLineFuncton fun, CGPoint point, float distance, CGPoint *point1,CGPoint *point2 );//point必须在fun上
void GetPointOnMathLineFunction ( MathLineFuncton fun, CGPoint point, float distance, CGPoint *point1,CGPoint *point2 ){
// 一元二次方程求解过程,高中数学
float M = fun.b - point.y;
float a = fun.k * fun.k + 1;
float b = -2 * ( point.x - fun.k * M );
float c = point.x * point.x + M * M - distance * distance;
float x1 = ( -b + sqrtf( ( b * b - 4 * a * c ) ) ) / ( 2 * a );
float x2 = ( -b - sqrtf( ( b * b - 4 * a * c ) ) ) / ( 2 * a );
if ( x1 <= x2 ){
float temp = x1;
x1 = x2;
x2 = temp;
}
( *point1 ).x = x1;
( *point1 ).y = fun.k * x1 + fun.b;
( *point2 ).x = x2;
( *point2 ).y = fun.k * x2 + fun.b;
}

@implementation CircleRecognizer

-(id)init{
self = [super init];
if ( nil != self ){
circleCore_ = [[CircleCore alloc]init];
minThresholdDegree_ = 200.0f;
CircleData small = CircleDataMake( CGPointZero, 5.0f);
circleCore_.smallCircle = small;
CircleData large = CircleDataMake( CGPointZero, 100.0f);
circleCore_.largeCircle = large;
weight_ = 0.3f;
weight2_ = 0.6f;
tangentLineLength_ = 2.0;
}
return self;
}
-(id)initWithTarget:(id)target action:(SEL)action{
self = [super initWithTarget:target action:action];
if ( nil != self ){
circleCore_ = [[CircleCore alloc]init];
minThresholdDegree_ = 200.0f;
CircleData small = CircleDataMake(CGPointZero, 5.0f);
circleCore_.smallCircle = small;
CircleData large = CircleDataMake(CGPointZero, 100.0f);
circleCore_.largeCircle = large;
tangentLineLength_ = 2.0f;
weight_ = 0.3f;
weight2_ = 0.6f;
}
return self;
}
-(void)dealloc{
[circleCore_ release];
[super dealloc];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
if ([self numberOfTouches] != 1) {
self.state = UIGestureRecognizerStateFailed;
return;
}
UITouch *touch = [touches anyObject];
tangentLineStart_ = [touch locationInView:self.view];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
if ([self numberOfTouches] != 1) {
self.state = UIGestureRecognizerStateFailed;
return;
}
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self.view];

if ( ! bTangentLineConfirm_ ){//没确定切线,先确定切线
CGFloat distance = DistanceBetweenPoints(point, tangentLineStart_);
if ( distance >= tangentLineLength_ ){
tangentLineEnd_ = point;
bTangentLineConfirm_ = YES;
}
}
else if ( ! bCircleConfirm_ ){//确定两个判断圆
MathLineFuncton line_1 = GetMathLineFunctionPPoint(tangentLineStart_, tangentLineEnd_);
CGPoint midPoint = GetMidPoint(tangentLineStart_, tangentLineEnd_);
MathLineFuncton line_2 = GetMathLineFunctionKPoint(line_1.k, midPoint);
CGFloat distance = ( circleCore_.largeCircle.radius - circleCore_.smallCircle.radius ) * weight_ + circleCore_.smallCircle.radius;
GetPointOnMathLineFunction(line_2, midPoint, distance, ¢er1_, ¢er2_);

bCircleConfirm_ = YES;
}
else if ( ! bDirectionConfirm_ ){//确定唯一判定圆
float distance1 = DistanceBetweenPoints( point, center1_ );
float distance2 = DistanceBetweenPoints( point, center2_ );
if ( distance1 > circleCore_.largeCircle.radius * weight2_ ){

CircleData smallcircle;
smallcircle.radius = circleCore_.smallCircle.radius;
smallcircle.center = center2_;
circleCore_.smallCircle = smallcircle;

CircleData largecircle;
largecircle.radius = circleCore_.largeCircle.radius;
largecircle.center = center2_;
circleCore_.largeCircle = largecircle;

bDirectionConfirm_ = YES;
}
else if ( distance2 > circleCore_.largeCircle.radius * weight2_ ){

CircleData smallcircle;
smallcircle.radius = circleCore_.smallCircle.radius;
smallcircle.center = center1_;
circleCore_.smallCircle = smallcircle;

CircleData largecircle;
largecircle.radius = circleCore_.largeCircle.radius;
largecircle.center = center1_;
circleCore_.largeCircle = largecircle;

bDirectionConfirm_ = YES;
}
}
else if ( !bZeroDegConfirm_ ){
float deg = DegreeAtCircleCenterAtPoint( circleCore_.smallCircle, tangentLineStart_ );
float deg2 = DegreeAtCircleCenterAtPoint( circleCore_.smallCircle, tangentLineEnd_ );
if ( deg < deg2 || ( 270.0 < deg && deg < 360.0f && 0.0f < deg2 && deg < 90.0f ) ){
wisetype = WT_CLOCK_WISE;
}
else{
wisetype = WT_ANTI_CLOCK_WISE;
}
zeroDegree_ = deg;
lastDegree_ = deg2 - deg;
while (lastDegree_ < 0.0f) {
lastDegree_ += 360.0f;
}

bZeroDegConfirm_ = YES;
}
else if ( bZeroDegConfirm_ ){
float deg = DegreeAtCircleCenterAtPoint(circleCore_.smallCircle, point);
deg = deg - zeroDegree_;
while (deg < 0.0f) {
deg += 360.0f;
}
sumdeg_ += ABS( lastDegree_ - deg );
lastDegree_ = deg;
float distance = DistanceBetweenPoints( point, circleCore_.smallCircle.center );
if ( distance > circleCore_.largeCircle.radius ){
self.state = UIGestureRecognizerStateFailed;
}
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
if ( sumdeg_ > minThresholdDegree_ ){
NSNumber * degree = [NSNumber numberWithFloat:sumdeg_];
NSNumber * wiseType = [NSNumber numberWithInt:wisetype];
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:degree,KDegreeKey,
wiseType,KWiseTypeKey,
nil];
NSNotification *notify = [NSNotification notificationWithName:kRecognizeCircleNotification object:self userInfo:dic];
[[NSNotificationCenter defaultCenter]postNotification:notify];
self.state = UIGestureRecognizerStateRecognized;
}
else{
self.state = UIGestureRecognizerStateFailed;
}
}
-(void)reset{
[super reset];
self.state = UIGestureRecognizerStatePossible;
bTangentLineConfirm_ = NO;
bCircleConfirm_ = NO;
bDirectionConfirm_ = NO;
bZeroDegConfirm_ = NO;
sumdeg_ = 0.0f;
wisetype = WT_NONE;
}
@end



收藏
我来说两句
发表评论
您还没有登录!请登录注册
所有评论(0
提示
sina weixin mail 回到顶部