iphone多点触摸机制及开发需注意的问题

        IPhone的成功,其支持多点触摸的电容屏触摸技术有不小的功劳,最近进行地图软件的移植开发,对多点触控进行了一些研究,在这里整理一下开发心得同大家分享。

        老的电阻式触摸屏(就是不支持多点触摸,需要用触控笔操作的),相对于鼠标的使用行为,其实差别不大,所以在windows消息里面,对触控消息,都还是沿用老的mousedown,mouseup,mousemove这三个函数处理,唯一和鼠标不一样的,就是

1. 没有鼠标左键右键的区分

2. 只要有mousemove消息,肯定先有mousedown,触摸屏上移动肯定要先点击了 

但是总体而言,单点触摸屏的消息机制同鼠标差别不大,每个事件只有一个坐标。

不过多点触摸就完全不一样了,同时要跟踪多个坐标变化,老的down up move消息模型不完全适用这种情况,必须要使用新的消息机制。

下面我们来看IOS使用的消息模型,如下:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

这里的Began Ended Moved, 咋一看同老的down up move消息模型差不多,不过理念上还是有很大不同。最基本的不同,就是传入的是多个触摸对象。

有了多个对象带来一个问题,时刻一检测到设备上A点,B点有触摸信号,时刻二检测到C点,D点有触摸信号,那么,是按住A点的手指运动到了C点,按住B点的手指运动到了D点,还是A到D,B到C呢?抑或是A到C,但是B点的手指抬起来,同时另一个手指按住了D? 乍一想,大家可能觉得运动是连续性的,可以判断呀,但是,连续性是理论上及哲学上的一个概念,物理设备的检测不存在连续性,(可以做一个实验,windows下响应mousemove消息,每响应一次就在坐标上画一个点,在快速移动鼠标的情况下,点是很离散的)我们只有通过各种算法推断两个时间段上触控对象的联系。

        在鼠标及单点触摸屏处理上,整个屏幕响应的点永远只有一个,因此老的所有鼠标事件,传入的参数只有坐标及其它一些状态值,而不会传入一个鼠标对象,那么对于多点触控,是不是只传入多个坐标点就可以了?  答案是,不行,如上所说,多点触控,实际上有了多个触控对象,我们需要跟踪这些对象.

        苹果的touch消息接口通过 touches 对象封装了一个触摸操作, 一个touches对象对应一个点的触摸操作, 多个点同时被按下就有多个touches对象, 注意我强调了同时, 如果两个指头是先后按住屏幕的, 会收到两个touchesBegan消息, 这很好理解, 但是此时要注意, 第二次 touchesBegan 发生时, 屏幕这时有两个点被按住, 但是touchesBegan 传入的 touches 集合只有一个touch成员. 同样, 手指在屏幕移动时, 如果两个手指都按住屏幕, 但是只有一个手指移动, 另一个按着不动, touchesMoved 同样也只传入一个touch对象, 所以一定要注意, 依靠touch事件中的 (NSSet *)touches 参数个数判断是单点还是多点触摸, 是不可靠的

        在很流行的iphone入门开发书籍 《Iphone3 开发基础教程》中关于多点触摸的例程 PinchMe, 就使用touches 的个数来判断两点缩放操作,这是不严谨的

//

//  PinchMeViewController.m

//  PinchMe

//

//  Created by jeff on 4/28/09.

//  Copyright Jeff LaMarche 2009. All rights reserved.

//


#import "PinchMeViewController.h"

#import "CGPointUtils.h"

@implementation PinchMeViewController

@synthesize label;

@synthesize initialDistance;

- (void)eraseLabel {

    label.text = @"";

}

- (void)viewDidUnload {

// Release any retained subviews of the main view.

// e.g. self.myOutlet = nil;

    self.label = nil;

}

- (void)dealloc {

    [label release];

    [super dealloc];

}

#pragma mark -

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

    if ([touches count] == 2) {

        NSArray *twoTouches = [touches allObjects];

        UITouch *first = [twoTouches objectAtIndex:0];

        UITouch *second = [twoTouches objectAtIndex:1];

        initialDistance = distanceBetweenPoints(

                                                [first locationInView:self.view], 

                                                [second locationInView:self.view]);

    }

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

 

    if ([touches count] == 2) {

        NSArray *twoTouches = [touches allObjects];

        UITouch *first = [twoTouches objectAtIndex:0];

        UITouch *second = [twoTouches objectAtIndex:1];

        CGFloat currentDistance = distanceBetweenPoints(

                                                        [first locationInView:self.view],

                                                        [second locationInView:self.view]);

 

        if (initialDistance == 0)

            initialDistance = currentDistance; 

        else if (currentDistance - initialDistance > kMinimumPinchDelta) {

            label.text = @"Outward Pinch";

            [self performSelector:@selector(eraseLabel) 

                       withObject:nil 

                       afterDelay:1.6f];

        }

        else if (initialDistance - currentDistance > kMinimumPinchDelta) {

            label.text = @"Inward Pinch";

            [self performSelector:@selector(eraseLabel) 

                       withObject:nil 

                       afterDelay:1.6f];

        }

    }

}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

    initialDistance = 0;

@end


把它的例子编译到设备上面,你会发现,如果两个手指不是同时落到屏幕,或是滑动手指时保持一个手指不动,缩放操作是无效的。

如果是我设计这个接口,我可能会考虑不管屏幕触控对象有没有移动,我都会吧所有按下的触摸对象传入函数,然后设计一个标志位标识哪些对象是活动的,哪些对象不活动。

不过既然苹果没有按照这个设想设计接口,也有解决方法,就是我们自己保存touch队列来管理触摸对象。

我们可以在负责按下消息的 touchesBegan 函数中,存储传入的touches 对象到我们自己的touch队列,在touchesEnded 时,在这个队列中删除传入的touches对象, 在touchesMoved中,以我们自己维护的touch队列成员个数来判断当前是否按下多点,而不是依靠传入的 touches对象, 

代码如下:

TouchRecord  是我们自定义的touch对象

@interface TouchRecord : NSObject

{
 id m_id;  // 用以唯一标识touch对象,实际他是传入的touches集合中touch对象的指针
 CGPoint m_point;  // touch对象的位置
}
@synthesize m_point;
@synthesize m_phase;
{
    return [self initWithTouch:nil pointInView:CGPointMake(0.0, 0.0)];
}
- (id)initWithTouch:(UITouch*)aTouch pointInView:(CGPoint)point
{
    self = [super init];
    if (self) {
        /* class-specific initialization goes here */
  m_id = aTouch;
  m_point = point;
  m_phase = aTouch.phase;
  
    }
    return self;
}
    [super dealloc];
}

 for (UITouch* touch in touches) {
  
  CGPoint point = [touch locationInView:self];
  TouchRecord* record = [[TouchRecord alloc] initWithTouch:touch pointInView:point];
  [m_arrayTouch addObject:record];
  [record release];
 }
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
 for (UITouch* touch in touches) {
  
  CGPoint point = [touch locationInView:self];
  
  for (TouchRecord* record in m_arrayTouch) {
   if (touch == record.m_id) {
    record.m_point = point;
    record.m_phase = touch.phase;
    break;
   }
  }
 }
}
// Handles the end of a touch event.
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
 for (UITouch* touch in touches) {
  
  for (TouchRecord* record in m_arrayTouch) {
   if (touch == record.m_id) {
    [m_arrayTouch removeObject:record];
    break;
   }
  }
 }
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
 for (UITouch* touch in touches) {
  
  for (TouchRecord* record in m_arrayTouch) {
   if (touch == record.m_id) {
    [m_arrayTouch removeObject:record];
    break;
   }
  }
 }
}

 UITouchPhase m_phase;  //touch对象的状态

 

@implementation TouchRecord

@synthesize m_id;

- (id)init

- (void)dealloc {

@end

 

//touch事件

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

// Handles the continuation of a touch.

m_arrayTouch 在view中定义为 NSMutableArray* m_arrayTouch;  使我们自己维护的touch集合,成员是TouchRecord 对象 

通过这种管理方式,我们就能够正确判断当前的多点触摸状态。

       在 IOS 3.2 SDK后,苹果提供了手势消息 UIGestureRecognizer, 可以对常用的两点缩放,旋转,轻扫等手势直接封装成对应的消息,有兴趣的读者可以查阅相关资料。这简化了手势操作的编程,大家可以不同began move这类消息直接打交道, 但是仅支持 IOS3.2以上系统, 3.2看起来不算新,但要知道 对于一些古老设备(比如itouch 1代),最高只能升级到 IOS 3.1.2系统的,同时这类消息也有局限性,在多点触摸时代,程序中经常需要自定义一些动作,所以掌握自己管理touch对象方法也是非常必要的。

最后,大家通过编写测试代码,可很容易测得, Iphone,itouch上,最多可支持5点触摸。而在Ipad上呢, 最多可支持11个点的触摸

至于现在使用到11个点的应用,大家可去试试钢琴类的应用程序,同时按下11个音,第12个就按不出来了。


----

转自:iphone多点触摸机制及开发需注意的问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值