目标
了解多点触摸术语
理解响应者链
体系结构
多点触摸的基本应用
1、了解多点触摸术语
触摸事件
指手指放到 iOS 设备屏幕上 从屏幕上拖动或抬起。
iOS中触摸事件,基于多点触摸模型
多点触摸序列 NSSet*touches
一个或多个和屏幕接触的手指,识别为多点触摸序列的一部分
从第一个手指碰到屏幕开始,直到最后一个手指离开屏幕结束
注意:触摸不关心,触摸的时间;只关心触摸的手指数目;触摸次数;触摸的方向;
Touch 是指手指放到屏幕上
触摸数量(touch):等于手指的数量,能够跟踪用户所有的手指,最多11个
点击(tap):手指触摸屏幕、立即离开,则触发轻击
检测到多点触摸,轻击次数置为1-------tapCount
下图表示一次触摸事件:
包含完整触摸事件的多个状态:
开始(Began)、移动(Moved)、结束(Ended)、取消(Cancelled)
手势
手势:是为简化 触摸操作,推出的一组对象.手势是约定俗成的。
如果使用 手势对象,就不需要在 Began Moved 等方法中写代码。
手势指从用户用一个或多个手指接触屏幕时开始,直到手指离开屏幕为止所发生的所有事件。无论多长时间,只要你还有一个或多个手指在屏幕上,这个手势就存在。(除非来电话会中断该手势)
手势通过一系列 事件 内在系统来传递。用户与设备屏幕交互时生成事件。事件包含与发生的一次或多次触摸相关的信息。
每次接触屏幕,都要传递两个参数:
UITouch:多个触摸手指(触摸的顺序、触摸的位置)
UIEvent:一个事件
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
手势识别器
是实现所有手势的对象;
是一个 UIGestureRecognizer 子类的对象;
观察用户操作,并识别用户何时做了预先设置好的几种手势;
可以用于程序中任何视图中;
不需要编写手势处理方法(touchesBegin:withEvent:…);
可以识别如下手势:
1、拍击 UITapGestureRecognizer (任意次数的拍击) 2、向里或向外捏 UIPinchGestureRecognizer (用于缩放)
3、摇动或者拖拽 UIPanGestureRecognizer
4、擦碰 UISwipeGestureRecognizer(以任意方向)
5、旋转 UIRotationGestureRecognizer(手指朝相反方向移动) 6、长按 UILongPressGestureRecognizer
2、理解响应者链
响应者链
第一响应者是用户正在交互的对象(IB 中叫 FirstResponder)
以UIResponder为超类的任何类可以成为响应者
如果第一响应者不处理事件,事件会被发送到 “父(上级)响应者”
事件最终会被发到UIApplication,如果它不处理事件,将会被丢弃
3、体系结构
处理流程
当一个或多个手指触摸屏幕的时候发出发出:
-(void)touchesBegin: (NSSet*)touches withEvent: 消息
当一个或多个手指在屏幕移动的时候发出:
-(void)touchesMoved:withEvent: 消息
当一个或多个手指离开屏幕的时候发出:
-(void)touchesEnded:withEvent: 消息
当触摸序列被电话呼入等事件取消的时候发出:
-(void)touchesCancelled:withEvent:消息
任何视图组件,都继承了 UIResponder 类,都可以调用触摸事件方法
两个参数:
基础代码
UITouch*currentTouch = [touches anyObject]; //得到任意一个触摸对象
Int phaseState = currentTouch.phase; //当前触摸事件的状态
CGPointpopMoved = [currentTouch locationInView:self.view]; //得到当前触摸事件在当前视图中的位置
4、多点触摸的基本应用
检测单击或双击
单击事件 + 多触击事件
1.两个触摸事件发生的间隔时间很短
2.也只有当它们和同一个 视图相关联时
在 touchesEnded:withEvent: 触摸结束的时候,
判断击打次数,使用UITouch的tapCount方法
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//得到手指个数
NSInteger ntouchCount = [touches count];
self.lblFingers.text = [NSString stringWithFormat:@"有 %i 根手指",ntouchCount];
//得到点击次数
self.lblTapCounts.text = [NSString stringWithFormat:@"%i 次点击 %f 秒",currentTouch.tapCount,currentTouch.timestamp];
}
//检查手指数目
-(void)checkTouches:(NSSet *)touches
{
UITouch*currentTouch = [touches anyObject];
//根据当前手势的不同阶段,打印不同的信息
switch(currentTouch.phase){
caseUITouchPhaseBegan:
{
//potBegin 开始点击的位置
self.potBegin = [currentTouch locationInView:self.view];
}
break;
caseUITouchPhaseMoved:{
}
break;
caseUITouchPhaseEnded:{
self.lblEnd.text = @"触摸结束";
[self performSelector:@selector(eraseLabel:) withObject:self.lblEnd afterDelay:0.6f];
}
break;
caseUITouchPhaseCancelled:{
}
break;
default:
break;
}
}
检测碰擦手势(Swipe)
*.h 文件中
//触摸的开始坐标
@property(nonatomic)CGPoint beginPoint;
//定义用户滑动最少多少像素算一个手势
#define kMinSwipLength 25
//定义滑动时,偏移最多多少像素的误差
#define kMaxVariance 6
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *currentTouch = [touches anyObject];
//移动后的点
CGPointpopMoved = [currentTouch locationInView:self.view];
floatdeltaX = fabsf(self.potBegin.x - popMoved.x);
floatdeltaY = fabsf(self.potBegin.y - popMoved.y);
//判断滑动方向
if((deltaX > kMinSwipLength)&&(deltaY< kMaxVariance)) {
self.lblSwipe.text = @"水平移动";
//调用当前类的方法
[self performSelector:@selector(eraseLabel:) withObject:self.lblSwipe afterDelay:0.6f];
}
if((deltaY > kMinSwipLength)&&(deltaX< kMaxVariance)){
self.lblSwipe.text=@"垂直移动";
[self performSelector:@selector(eraseLabel:) withObject:self.lblSwipe afterDelay:0.6f];
}
}
移动一个按钮:
单独新建一个类,继承 UIButton,重写手势方法
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!dragEnable) {
return;
}
UITouch *touch = [touches anyObject];
beginPoint = [touch locationInView:self];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!dragEnable) {
return;
}
UITouch *touch = [touches anyObject];
CGPoint nowPoint = [touch locationInView:self];
float offsetX = nowPoint.x - beginPoint.x;
float offsetY = nowPoint.y - beginPoint.y;
self.center = CGPointMake(self.center.x+offsetX, self.center.y+offsetY);
}
使用手势识别器
1)创建手势实例。当创建手势时,指定一个回调方法,当手势开始,改变、或结束时,回调方法被调用。
2)添加到需要识别的View中。每个手势只对应一个View,当屏幕触摸在View的边界内时,如果手势和预定的一样,那就会回调方法。
注意:一个手势只能对应一个View,但是一个View可以有多个手势。建议在真机上运行这些手势,模拟器操作不太方便。
使用擦碰识别器(UISwipeGestureRecognizer)
- (void)viewDidLoad
{
[super viewDidLoad];
//直接使用手势判断对象,确定手势并调用方法
UISwipeGestureRecognizer*vertical = [[UISwipeGestureRecognizeralloc]initWithTarget:self action:@selector(reportYSwipe:)];
//设置手势方向
vertical.direction= UISwipeGestureRecognizerDirectionUp|UISwipeGestureRecognizerDirectionDown;
//把手势添加到当前视图中
[self.view addGestureRecognizer:vertical];
//再添加一个手势
UISwipeGestureRecognizer*horizontal = [[UISwipeGestureRecognizeralloc]initWithTarget:self action:@selector(reportXSwipe:)];
//设置手势方向
horizontal.direction= UISwipeGestureRecognizerDirectionLeft|UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:horizontal];
}
#pragmamark - 手势发生后调用的方法
-(void)reportXSwipe:(UIGestureRecognizer *)recognizer
{
self.lbltext.text = @"横向扫过";
[selfperformSelector:@selector(eraseText) withObject:nil afterDelay:2.0f];
}
-(void)reportYSwipe:(UIGestureRecognizer *)recognizer
{
self.lbltext.text = @"竖向扫过";
[selfperformSelector:@selector(eraseText) withObject:nil afterDelay:2.0f];
}
//抹除文本标签中的文字
-(void)eraseText
{
self.lbltext.text = @" ";
}
检查多根手指触摸
- (void)viewDidLoad
{
[super viewDidLoad];
//循环5次,生成5个SwipGesturerecognizer,处理5根手指
for(NSUIntegertouchCount = 1;touchCount <= 5;touchCount++) {
UISwipeGestureRecognizer*vertical;
vertical = [[UISwipeGestureRecognizeralloc]
initWithTarget:self action:@selector(reportVerticalSwipe:)];
vertical.direction= UISwipeGestureRecognizerDirectionUp
| UISwipeGestureRecognizerDirectionDown;
vertical.numberOfTouchesRequired= touchCount;
[self.view addGestureRecognizer:vertical];
UISwipeGestureRecognizer*horizontal;
horizontal = [[UISwipeGestureRecognizeralloc]
initWithTarget:self action:@selector(reportHorizontalSwipe:)];
horizontal.direction= UISwipeGestureRecognizerDirectionLeft
| UISwipeGestureRecognizerDirectionRight;
horizontal.numberOfTouchesRequired= touchCount;
[self.view addGestureRecognizer:horizontal];
}
}
- (void)eraseText
{
self.label.text = @"";
}
- (NSString *)descriptionForTouchCount:(NSUInteger)touchCount
{
switch(touchCount) {
case1:
return@"一根手指";
case2:
return @"两根手指";
case3:
return @"三根手指";
case4:
return @"四根手指";
case5:
return @"五根手指";
default:
return @"";
}
}
- (void)reportHorizontalSwipe:(UIGestureRecognizer *)recognizer
{
self.label.text = [NSString stringWithFormat:@"%@ 根手指水平方向扫过",
[self descriptionForTouchCount:[recognizer numberOfTouches]]];
[selfperformSelector:@selector(eraseText) withObject:nil afterDelay:2];
}
- (void)reportVerticalSwipe:(UIGestureRecognizer *)recognizer
{
self.label.text = [NSString stringWithFormat:@"%@ 根手指垂直方向扫过",
[self descriptionForTouchCount:[recognizer numberOfTouches]]];
[selfperformSelector:@selector(eraseText) withObject:nil afterDelay:2];
}
-----------------------------
使用点击识别器(UITapGestureRecognizer)
- (void)viewDidLoad
{
[super viewDidLoad];
//创建 点击识别器 对象
UITapGestureRecognizer *singleTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(singleTap)];
singleTap.numberOfTapsRequired = 1; //设置点击次数为 1 次
singleTap.numberOfTouchesRequired = 1; //设置 1 根手指
[self.view addGestureRecognizer:singleTap];
UITapGestureRecognizer *doubleTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(doubleTap)];
doubleTap.numberOfTapsRequired = 2;
doubleTap.numberOfTouchesRequired = 1;
[self.view addGestureRecognizer:doubleTap];
//告诉单击感知器,如果双击操作未识别时触发自己的操作
[singleTap requireGestureRecognizerToFail:doubleTap];
UITapGestureRecognizer*tripleTap =
[[UITapGestureRecognizeralloc]initWithTarget:self
action:@selector(tripleTap)];
tripleTap.numberOfTapsRequired= 3;
tripleTap.numberOfTouchesRequired= 1;
[self.view addGestureRecognizer:tripleTap];
[doubleTap requireGestureRecognizerToFail:tripleTap];
UITapGestureRecognizer*quadrupleTap =
[[UITapGestureRecognizeralloc]initWithTarget:self
action:@selector(quadrupleTap)];
quadrupleTap.numberOfTapsRequired= 4;
quadrupleTap.numberOfTouchesRequired= 1;
[self.view addGestureRecognizer:quadrupleTap];
[tripleTap requireGestureRecognizerToFail:quadrupleTap];
//点击手势失败链。先检查 4 次点击是否有识别器,如果没有,检查 3 次点击是否有识别器….
}
- (void)singleTap
{
self.singleLabel.text = @"Single Tap Detected";
[selfperformSelector:@selector(clearLabel:)
withObject:self.singleLabel
afterDelay:1.6f];
}
- (void)doubleTap
{
self.doubleLabel.text = @"Double Tap Detected";
[selfperformSelector:@selector(clearLabel:)
withObject:self.doubleLabel
afterDelay:1.6f];
}
- (void)tripleTap
{
self.tripleLabel.text = @"Triple Tap Detected";
[selfperformSelector:@selector(clearLabel:)
withObject:self.tripleLabel
afterDelay:1.6f];
}
- (void)quadrupleTap
{
self.quadrupleLabel.text = @"Quadruple Tap Detected";
[selfperformSelector:@selector(clearLabel:)
withObject:self.quadrupleLabel
afterDelay:1.6f];
}
- (void)clearLabel:(UILabel *)label
{
label.text= @"";
}
使用缩放识别器(UIPinchGestureRecognizer)
@property(non
-(void)viewDidLoad
{
UIPinchGestureRecognizer *pich = [[UIPinchGestureRecognizer alloc]initWithTarget:self
action:@selector(doPinch:)];
[self.view addGestureRecognizer:pich];
}
-(void)doPinch: (UIPinchGestureRecognizer *)pinch
{
if(pinch.state == UIPinchGestureRecognizerStateBegan)
{
initFontSize = label1.font.pointsize;
}
else
{
label1.font = [label1.fontfontWithSize:initFontSize*pinch.scale];
}
}
同时实现“缩放”“旋转”手势识别器
1、头文件实现协议
<UIGestureRecognizerDelegate>
2、
//scale缩小
//rotation 旋转
@implementationBIDViewController {
CGFloatscale, previousScale;
CGFloatrotation, previousRotation;
}
- (void)viewDidLoad
{
[superviewDidLoad];
previousScale= 1;
UIImage*image = [UIImageimageNamed:@"yosemite-meadows.png"];
self.imageView = [[UIImageView alloc] initWithImage:image];
//能否配合使用
self.imageView.userInteractionEnabled = YES;
self.imageView.center = CGPointMake(160, 160);
[self.view addSubview:self.imageView];
UIPinchGestureRecognizer*pinchGesture =
[[UIPinchGestureRecognizeralloc]initWithTarget:self action:@selector(doPinch:)];
pinchGesture.delegate= self;
[self.imageView addGestureRecognizer:pinchGesture];
UIRotationGestureRecognizer*rotationGesture =
[[UIRotationGestureRecognizeralloc]initWithTarget:self action:@selector(doRotate:)];
rotationGesture.delegate= self;
[self.imageView addGestureRecognizer:rotationGesture];
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizershouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer
{
returnYES;
}
- (void)transformImageView
{
CGAffineTransformt = CGAffineTransformMakeScale(scale * previousScale,
scale* previousScale);
t = CGAffineTransformRotate(t,rotation+ previousRotation);
self.imageView.transform = t;
}
- (void)doPinch:(UIPinchGestureRecognizer *)gesture
{
scale= gesture.scale;
[selftransformImageView];
if(gesture.state== UIGestureRecognizerStateEnded){
previousScale= scale* previousScale;
scale= 1;
}
}
- (void)doRotate:(UIRotationGestureRecognizer *)gesture
{
rotation= gesture.rotation;
[selftransformImageView];
if(gesture.state== UIGestureRecognizerStateEnded){
previousRotation= rotation+ previousRotation;
rotation= 0;
}
}
总结:
1、补充图形处理的方法:上下文栈、界面重绘(作业,动画机器猫)
2、触摸、手势概念
3、使用手势对象(作业:完成图片的缩放、旋转)
4、了解手势执行的过程(思考:如何判断移动方向)
检查 猜图游戏 项目
补充:
1、NSObject 类中让方法延迟执行
2、擦碰(滑动)识别器
3、点击识别器
4、上下文状态栈,避免效果冲突
- (void)drawRect:(CGRect)rect
{
// draw a rounded rect bezier path filled with blue
CGContextRef aRef = UIGraphicsGetCurrentContext();
//把当前的上下文状态保存到 状态栈中
CGContextSaveGState(aRef);
//你自己的绘图代码
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:5.0f];
[bezierPath setLineWidth:5.0f];
[[UIColor blackColor] setStroke];
UIColor *fillColor = [UIColor colorWithRed:0.529 green:0.808 blue:0.922 alpha:1]; // color equivalent is #87ceeb
[fillColor setFill];
[bezierPath stroke];
[bezierPath fill];
//绘制完毕,从状态栈中还原
CGContextRestoreGState(aRef);
}
5、了解界面重绘
//通知 MyView 让它重绘整个页面
//该方法自动调用 - (void)drawRect:(CGRect)rect
//整个界面重绘
[self.myView setNeedsDisplay];
//在指定范围内,重绘,节约内存
//[self.myViewsetNeedsDisplayInRect:(CGRect)]