移动开发:iphone开发之触摸事件详解

转:http://blog.sina.com.cn/s/blog_8988732e01012eaf.html

iPhoneOS中的触摸事件基于多点触摸模型。用户不是通过鼠标和键盘,而是通过触摸设备的屏幕来操作对象、输入数据、以及指示自己的意图。iPhoneOS将一个或多个和屏幕接触的手指识别为多点触摸序列的一部分,该序列从第一个手指碰到屏幕开始,直到最后一个手指离开屏幕结束。iPhoneOS通过一个多点触摸序列来跟踪与屏幕接触的手指,记录每个手指的触摸特征,包括手指在屏幕上的位置和发生触摸的时间。应用程序通常将特定组合的触摸识别为手势,并以用户直觉的方式来进行响应,比如对收缩双指距离的手势,程序的响应是缩小显示的内容;对轻拂屏幕的手势,则响应为滚动显示内容。

请注意:手指在屏幕上能达到的精度和鼠标指针有很大的不同。当用户触击屏幕时,接触区域实际上是椭圆形的,而且比用户想像的位置更靠下一点。根据触摸屏幕的手指、手指的尺寸、手指接触屏幕的力量、手指的方向、以及其它因素的不同,其“接触部位”的尺寸和形状也有所不同。底层的多点触摸系统会分析所有的这些信息,为您计算出单一的触点。

很多UIKit类对多点触摸事件的处理方式不同于它的对象实例,特别是像UIButtonUISlider这样的UIControl的子类。这些子类的对象—被称为控件对象—只接收特定类型的手势,比如触击或向特定方向拖拽。控件对象在正确配置之后,会在某种手势发生后将动作消息发送给目标对象。其它的UIKit类则在其它的上下文中处理手势,比如UIScrollView可以为表格视图和具有很大内容区域的文本视图提供滚动行为。

某些应用程序可能不需要直接处理事件,它们可以依赖UIKit类实现的行为。但是,如果您创建了UIView定制子类—这是iPhoneOS系统开发的常见模式—且希望该视图响应特定的触摸事件,就需要实现处理该事件所需要的代码。而且,如果您希望一个UIKit对象以不同的方式响应事件,就必须创建框架类的子类,并重载相应的事件处理方法。

事件和触摸

在iPhone OS中,触摸动作是指手指碰到屏幕或在屏幕上移动,它是一个多点触摸序列的一部分。比如,一个pinch-close手势就包含两个触摸动作:即屏幕上的两个手指从相反方向靠近对方。一些单指手势则比较简单,比如触击、双击、或轻拂(即用户快速碰擦屏幕)。应用程序也可以识别更为复杂的手势,举例来说,如果一个应用程序使用具有转盘形状的定制控件,用户就需要用多个手指来“转动”转盘,以便进行某种精调。

事件是当用户手指触击屏幕及在屏幕上移动时,系统不断发送给应用程序的对象。事件对象为一个多点触摸序列中所有触摸动作提供一个快照,其中最重要的是特定视图中新发生或有变化的触摸动作。一个多点触摸序列从第一个手指碰到屏幕开始,其它手指随后也可能触碰屏幕,所有手指都可能在屏幕上移动。当最后一个手指离开屏幕时,序列就结束了。在触摸的每个阶段,应用程序都会收到事件对象。

触摸信息有时间和空间两方面,时间方面的信息称为阶段(phrase),表示触摸是否刚刚开始、是否正在移动或处于静止状态,以及何时结束—也就是手指何时从屏幕举起(参见图3-1)。触摸信息还包括当前在视图或窗口中的位置信息,以及之前的位置信息(如果有的话)。当一个手指接触屏幕时,触摸就和某个窗口或视图关联在一起,这个关联在事件的整个生命周期都会得到维护。如果有多个触摸同时发生,则只有和同一个视图相关联的触摸会被一起处理。类似地,如果两个触摸事件发生的间隔时间很短,也只有当它们和同一个视图相关联时,才会被处理为多触击事件。

图3-1 多点触摸序列和触摸阶段

A multi-touch sequence and touch phases

在iPhone OS中,一个UITouch对象表示一个触摸,一个UIEvent对象表示一个事件。事件对象中包含与当前多点触摸序列相对应的所有触摸对象,还可以提供与特定视图或窗口相关联的触摸对象(参见图3-2)。在一个触摸序列发生的过程中,对应于特定手指的触摸对象是持久的,在跟踪手指运动的过程中,UIKit会对其进行修改。发生改变的触摸属性变量有触摸阶段、触摸在视图中的位置、发生变化之前的位置、以及时间戳。事件处理代码通过检查这些属性的值来确定如何响应事件。

图3-2 UIEvent对象及其UITouch对象间的关系

Relationship of a UIEvent object and its UITouch objects

系统可能随时取消多点触摸序列,进行事件处理的应用程序必须做好正确响应的准备。事件的取消可能是由于重载系统事件引起的,电话呼入就是这样的例子。

事件的传递

系统将事件按照特定的路径传递给可以对其进行处理的对象。如“核心应用程序架构”部分描述的那样,当用户触摸设备屏幕时,iPhoneOS会将它识别为一组触摸对象,并将它们封装在一个UIEvent对象中,放入当前应用程序的事件队列中。事件对象将特定时刻的多点触摸序列封装为一些触摸对象。负责管理应用程序的UIApplication单件对象将事件从队列的顶部取出,然后派发给其它对象进行处理。典型情况下,它会将事件发送给应用程序的键盘焦点窗口—即拥有当前用户事件焦点的窗口,然后代表该窗口的UIWindow对象再将它发送给第一响应者进行处理(第一响应者在 “响应者对象和响应者链”部分中描述)

应用程序通过触碰测试(hit-testing)来寻找事件的第一响应者,即通过递归调用视图层次中视图对象的hitTest:withEvent:方法来确认发生触摸的子视图。触摸对象的整个生命周期都和该视图互相关联,即使触摸动作最终移动到该视图区域之外也是如此。“事件处理技巧”部分对触碰测试在编程方面的一些隐含意义进行讨论。

UIApplication对象和每个UIWindow对象都在sendEvent:方法(两个类都声明了这个方法)中派发事件。由于这些方法是事件进入应用程序的通道,所以,您可以从UIApplicationUIWindow派生出子类,重载其sendEvent:方法,实现对事件的监控或执行特殊的事件处理。但是,大多数应用程序都不需要这样做。

响应者对象和响应者链

响应者对象是可以响应事件并对其进行处理的对象。UIResponder是所有响应者对象的基类,它不仅为事件处理,而且也为常见的响应者行为定义编程接口。UIApplicationUIView、和所有从UIView派生出来的UIKit类(包括UIWindow)都直接或间接地继承自UIResponder类。

第一响应者是应用程序中当前负责接收触摸事件的响应者对象(通常是一个UIView对象)。UIWindow对象以消息的形式将事件发送给第一响应者,使其有机会首先处理事件。如果第一响应者没有进行处理,系统就将事件(通过消息)传递给响应者链中的下一个响应者,看看它是否可以进行处理。

响应者链是一系列链接在一起的响应者对象,它允许响应者对象将处理事件的责任传递给其它更高级别的对象。随着应用程序寻找能够处理事件的对象,事件就在响应者链中向上传递。响应者链由一系列“下一个响应者”组成,其顺序如下:

  1. 第一响应者将事件传递给它的视图控制器(如果有的话),然后是它的父视图。

  2. 类似地,视图层次中的每个后续视图都首先传递给它的视图控制器(如果有的话),然后是它的父视图。

  3. 最上层的容器视图将事件传递给UIWindow对象。
  4. UIWindow对象将事件传递给UIApplication单件对象。

如果应用程序找不到能够处理事件的响应者对象,则丢弃该事件。

响应者链中的所有响应者对象都可以实现UIResponder的某个事件处理方法,因此也都可以接收事件消息。但是,它们可能不愿处理或只是部分处理某些事件。如果是那样的话,它们可以将事件消息转送给下一个响应者,方法大致如下:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch* touch = [touches anyObject];
    NSUInteger numTaps = [touch tapCount];
    if (numTaps < 2) {
        [self.nextResponder touchesBegan:touches withEvent:event];
   } else {
        [self handleDoubleTap:touch];
   }
}

请注意:如果一个响应者对象将一个多点触摸序列的初始阶段的事件处理消息转发给下一个响应者(在touchesBegan:withEvent:方法中),就应该同样转发该序列的其它事件处理消息。

动作消息的处理也使用响应者链。当用户对诸如按键或分页控件这样的UIControl对象进行操作时,控件对象(如果正确配置的话)会向目标对象发送动作消息。但是,如果目标对象被指定为nil,应用程序就会像处理事件消息那样,把该动作消息路由给第一响应者。如果第一响应者没有进行处理,再发送给其下一个响应者,以此类推,将消息沿着响应者链向上传递。

调整事件的传递

UIKit为应用程序提供了一些简化事件处理、甚至完全关闭事件流的编程接口。下面对这些方法进行总结:

  • 关闭事件的传递。缺省情况下,视图会接收触摸事件。但是,您可以将其userInteractionEnabled属性声明设置为NO,关闭事件传递的功能。隐藏或透明的视图也不能接收事件。

  • 在一定的时间内关闭事件的传递。应用程序可以调用UIApplicationbeginIgnoringInteractionEvents方法,并在随后调用endIgnoringInteractionEvents方法来实现这个目的。前一个方法使应用程序完全停止接收触摸事件消息,第二个方法则重启消息的接收。某些时候,当您的代码正在执行动画时,可能希望关闭事件的传递。

  • 打开多点触摸的传递。 缺省情况下,视图只接收多点触摸序列的第一个触摸事件,而忽略所有其它事件。如果您希望视图处理多点触摸,就必须使它启用这个功能。在代码或InterfaceBuilder的查看器窗口中将视图的multipleTouchEnabled属性设置为YES,就可以实现这个目标。

  • 将事件传递限制在某个单独的视图上。 缺省情况下,视图的exclusiveTouch属性被设置为NO。将这个属性设置为YES会使相应的视图具有这样的特性:即当该视图正在跟踪触摸动作时,窗口中的其它视图无法同时进行跟踪,它们不能接收到那些触摸事件。然而,一个标识为“独占触摸”的视图不能接收与同一窗口中其它视图相关联的触摸事件。如果一个手指接触到一个独占触摸的视图,则仅当该视图是窗口中唯一一个跟踪手指的视图时,触摸事件才会被传递。如果一个手指接触到一个非独占触摸的视图,则仅当窗口中没有其它独占触摸视图跟踪手指时,该触摸事件才会被传递。

  • 将事件传递限制在子视图上。一个定制的UIView类可以通过重载hitTest:withEvent:方法来将多点触摸事件的传递限制在它的子视图上。这个技巧的讨论请参见“事件处理技巧”部分。

处理多点触摸事件

为了处理多点触摸事件,UIView的定制子类(比较不常见的还有UIApplicationUIWindow的定制子类)必须至少实现一个UIResponder的事件处理方法。本文的下面部分将对这些方法进行描述,讨论处理常见手势的方法,并展示一个处理复杂多点触摸事件的响应者对象实例,以及就事件处理的某些技术提出建议。

事件处理方法

在一个多点触摸序列发生的过程中,应用程序会发出一系列事件消息。为了接收和处理这些消息,响应者对象的类必须至少实现下面这些由UIResponder类声明的方法之一:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

在给定的触摸阶段中,如果发生新的触摸动作或已有的触摸动作发生变化,应用程序就会发送这些消息:

  • 当一个或多个手指触碰屏幕时,发送touchesBegan:withEvent:消息。

  • 当一个或多个手指在屏幕上移动时,发送touchesMoved:withEvent:消息。

  • 当一个或多个手指离开屏幕时,发送touchesEnded:withEvent:消息。

  • 当触摸序列被诸如电话呼入这样的系统事件所取消时,发送touchesCancelled:withEvent:消息。

上面这些方法都和特定的触摸阶段(比如UITouchPhaseBegan)相关联,该信息存在于UITouch对象的phase属性声明中。

每个与事件处理方法相关联的消息都有两个参数。第一个参数是一个UITouch对象的集合,表示给定阶段中新的或者发生变化的触摸动作;第二个参数是一个UIEvent对象,表示这个特定的事件。您可以通过这个事件对象得到与之相关联的所有触摸对象(allTouches),或者发生在特定的视图或窗口上的触摸对象子集。其中的某些触摸对象表示自上次事件消息以来没有发生变化,或虽然发生变化但处于不同阶段的触摸动作。

为了处理给定阶段的事件,响应者对象常常从传入的集合参数中取得一或多个UITouch对象,然后考察这些对象的属性或取得它们的位置(如果需要处理所有触摸对象,可以向该NSSet对象发送anyObject消息)。UITouch类中有一个名为locationInView:的重要方法,如果传入self参数值,它会给出触摸动作在响应者坐标系统中的位置(假定该响应者是一个UIView对象,且传入的视图参数不为nil)。另外,还有一个与之平行的方法,可以给出触摸动作之前位置(previousLocationInView:)。UITouch实例的属性还可以给出发生多少次触碰(tapCount)、触摸对象的创建或最后一次变化发生在什么时间(timestamp)、以及触摸处于什么阶段(phase)。

响应者类并不是必须实现上面列出的所有三个事件方法。举例来说,如果它只对手指离开屏幕感兴趣,则只需要实现touchesEnded:withEvent:方法就可以了。

在一个多点触摸序列中,如果响应者在处理事件时创建了某些持久对象,则应该实现touchesCancelled:withEvent:方法,以便当系统取消该序列的时候对其进行清理。多点触摸序列的取消常常发生在应用程序的事件处理遭到外部事件—比如电话呼入—破坏的时候。请注意,响应者对象同样应该在收到多点触摸序列的touchesEnded:withEvent:消息时清理之前创建的对象(“事件处理技巧”部分讨论了如何确定一个序列中的最后一个touch-up事件)。

处理单个和多个触碰手势

iPhone应用程序中一个很常见的手势是触击:即用户用手指触碰一个对象。响应者对象可以以一种方式响应单击,而以另外一种方式响应双击,甚至可能以第三种方式响应三次触击。您可以通过考察UITouch对象的tapCount属性声明值来确定用户在一个响应者对象上的触击次数,

取得这个值的最好地方是touchesBegan:withEvent:touchesEnded:withEvent:方法。在很多情况下,我们更倾向于后者,因为它与用户手指离开屏幕的阶段相对应。在触摸结束阶段(UITouchPhaseEnded)考察触击的次数可以确定手指是真的触击,而不是其它动作,比如手指接触屏幕后拖动的动作。

程序清单3-1展示了如何检测某个视图上是否发生双击。

程序清单3-1  检测双击手势

- (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event
{
    UITouch       *touch = [touches anyObject];
 
    if ([touch tapCount] == 2) {
        CGPoint tapPoint = [theTouch locationInView:self];
        // Process a double-tap gesture
    }
}

当一个响应者对象希望以不同的方式响应单击双击事件时,就会出现复杂的情况。举例来说,单击的结果可能是选定一个对象,而双击则可能是显示一个编辑视图,用于编辑被双击的对象。那么,响应者对象如何知道一个单击不是另一个双击的起始部分呢?我们接下来解释响应者对象如何借助上文刚刚描述的事件处理方法来处理这种情况:

  1. touchesEnded:withEvent:方法中,当触击次数为一时,响应者对象就向自身发送一个performSelector:withObject:afterDelay:消息,其中的选择器标识由响应者对象实现的、用于处理单击手势的方法;第二个参数是一个NSValueNSDictionary对象,用于保存相关的UITouch对象;时延参数则表示单击和双击手势之间的合理时间间隔。

    请注意:使用一个NSValue对象或字典来保存触摸对象是因为它们会保持传入的对象。然而,您自己在进行事件处理时,不应该对UITouch对象进行保持。

  2. touchesBegan:withEvent:方法中,如果触击次数为二,响应者对象会向自身发送一个cancelPreviousPerformRequestsWithTarget:消息,取消当前被挂起和延期执行的调用。如果触碰次数不为二,则在指定的延时之后,先前步骤中由选择器标识的方法就会被调用,以处理单击手势。

  3. touchesEnded:withEvent:方法中,如果触碰次数为二,响应者会执行处理双击手势的代码。

检测碰擦手势

水平和垂直的碰擦(Swipe)是简单的手势类型,您可以简单地在自己的代码中进行跟踪,并通过它们执行某些动作。为了检测碰擦手势,您需要跟踪用户手指在期望的坐标轴方向上的运动。碰擦手势如何形成是由您自己来决定的,也就是说,您需要确定用户手指移动的距离是否足够长,移动的轨迹是否足够直,还有移动的速度是否足够快。您可以保存初始的触碰位置,并将它和后续的touch-moved事件报告的位置进行比较,进而做出这些判断。

程序清单3-2展示了一些基本的跟踪方法,可以用于检测某个视图上发生的水平碰擦。在这个例子中,视图将触摸的初始位置存储在名为startTouchPosition的成员变量中。随着用户手指的移动,清单中的代码将当前的触摸位置和起始位置进行比较,确定是否为碰擦手势。如果触摸在垂直方向上移动得太远,就会被认为不是碰擦手势,并以不同的方式进行处理。但是,如果手指继续在水平方向上移动,代码就继续将它作为碰擦手势来处理。一旦碰擦手势在水平方向移动得足够远,以至于可以认为是完整的手势时,处理例程就会触发相应的动作。检测垂直方向上的碰擦手势可以用类似的代码,只是把x和y方向的计算互换一下就可以了。

程序清单3-2  在视图中跟踪碰擦手势

#define HORIZ_SWIPE_DRAG_MIN  12
#define VERT_SWIPE_DRAG_MAX    4
 
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    startTouchPosition = [touch locationInView:self];
}
 
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint currentTouchPosition = [touch locationInView:self];
 
    // If the swipe tracks correctly.
    if (fabsf(startTouchPosition.x - currentTouchPosition.x) >= HORIZ_SWIPE_DRAG_MIN &&
        fabsf(startTouchPosition.y - currentTouchPosition.y) <= VERT_SWIPE_DRAG_MAX)
    {
        // It appears to be a swipe.
        if (startTouchPosition.x < currentTouchPosition.x)
            [self myProcessRightSwipe:touches withEvent:event];
        else
            [self myProcessLeftSwipe:touches withEvent:event];
    }
    else
    {
        // Process a non-swipe event.
    }
}

处理复杂的多点触摸序列

触击和碰擦是简单的手势。如何处理更为复杂的多点触摸序列—实际上是解析应用程序特有的手势—取决于应用程序希望完成的具体目标。您可以跟踪所有阶段的所有触摸动作,记录触摸对象中发生变化的属性变量,并正确地改变内部的状态。

说明如何处理复杂的多点触摸序列的最好方法是通过实例。程序清单3-3展示一个定制的UIView对象如何通过在屏幕上动画移动“Welcome”标语牌来响应用户手指的移动,以及如何通过改变欢迎标语的语言来响应用户的双击手势(例子中的代码来自一个名为MoveMe的示例工程,进一步考察该工程可以更好地理解事件处理的上下文)。

程序清单3-3  处理复杂的多点触摸序列

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [[event allTouches] anyObject];
    // Only move the placard view if the touch was in the placard view
    if ([touch view] != placardView) {
        // On double tap outside placard view, update placard's display string
        if ([touch tapCount] == 2) {
            [placardView setupNextDisplayString];
        }
        return;
    }
    // "Pulse" the placard view by scaling up then down
    // Use UIView's built-in animation
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.5];
    CGAffineTransform transform = CGAffineTransformMakeScale(1.2, 1.2);
    placardView.transform = transform;
    [UIView commitAnimations];
 
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.5];
    transform = CGAffineTransformMakeScale(1.1, 1.1);
    placardView.transform = transform;
    [UIView commitAnimations];
 
    // Move the placardView to under the touch
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.25];
    placardView.center = [self convertPoint:[touch locationInView:self] fromView:placardView];
    [UIView commitAnimations];
}
 
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [[event allTouches] anyObject];
 
    // If the touch was in the placardView, move the placardView to its location
    if ([touch view] == placardView) {
        CGPoint location = [touch locationInView:self];
        location = [self convertPoint:location fromView:placardView];
        placardView.center = location;
        return;
    }
}
 
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [[event allTouches] anyObject];
 
    // If the touch was in the placardView, bounce it back to the center
    if ([touch view] == placardView) {
        // Disable user interaction so subsequent touches don't interfere with animation
        self.userInteractionEnabled = NO;
        [self animatePlacardViewToCenter];
        return;
    }
}

请注意:对于通过描画自身的外观来响应事件的定制视图,在事件处理方法中通常应该只是设置描画状态,而在drawRect:方法中执行所有的描画操作。如果需要了解更多关于描画视图内容的方法,请参见“图形和描画”部分。

 

事件处理技巧

下面是一些事件处理技巧,您可以在自己的代码中使用。

  • 跟踪UITouch对象的变化

    在事件处理代码中,您可以将触摸状态的相关位置保存下来,以便在必要时和变化之后的UITouch实例进行比较。作为例子,假定您希望将每个触摸对象的最后位置和其初始位置进行比较,则在touchesBegan:withEvent:方法中,您可以通过locationInView:方法得到每个触摸对象的初始位置,并以UITouch对象的地址作为键,将它们存储在CFDictionaryRef封装类型中;然后,在touchesEnded:withEvent:方法中,可以通过传入UITouch对象的地址取得该对象的初始位置,并将它和当前位置进行比较(您应该使用CFDictionaryRef类型,而不是NSDictionary对象,因为后者需要对其存储的项目进行拷贝,而UITouch类并不采纳NSCopying协议,该协议在对象拷贝过程中是必须的)。

  • 对子视图或层上的触摸动作进行触碰测试

    定制视图可以用UIViewhitTest:withEvent:方法或CALayerhitTest:方法来寻找接收触摸事件的子视图或层,进而正确地处理事件。下面的例子用于检测定制视图的层中的“Info”图像是否被触碰。

    - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
    
        CGPoint location = [[touches anyObject] locationInView:self];
    
        CALayer *hitLayer = [[self layer] hitTest:[self convertPoint:location fromView:nil]];
    
     
    
        if (hitLayer == infoImage) {
    
            [self displayInfo];
    
        }
    
    }
    

    如果您有一个携带子视图的定制视图,就需要明确自己是希望在子视图的级别上处理触摸事件,还是在父视图的级别上进行处理。如果子视图没有实现touchesBegan:withEvent:touchesEnded:withEvent:、或者touchesMoved:withEvent:方法,则这些消息就会沿着响应者链被传播到父视图。然而,由于多次触碰和多点触摸事件与发生这些动作所在的子视图是互相关联的,所以父视图不会接收到这些事件。为了保证能接收到所有的触摸事件,父视图必须重载hitTest:withEvent:方法,并在其中返回其本身,而不是它的子视图。

  • 确定多点触摸序列中最后一个手指何时离开

    当您希望知道一个多点触摸序列中的最后一个手指何时从视图离开时,可以将传入的集合参数中包含的UITouch对象数量和UIEvent参数对象中与该视图关联的触摸对象数量相比较。请看下面的例子:

    - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
    
        if ([touches count] == [[event touchesForView:self] count]) {
    
            // last finger has lifted....
    
        }
    
    }
    

 

看完文档我们也许还不能完全掌握ios触摸事件,下面我们结合一个实例来继续学习。

新建一个项目SwitchByGesture,添加一个UIViewController命名为SecondViewController。

在ViewController.m中添加下面代码:

 


我们实现了touchBegan方法,在该方法中我们记住触摸事件其实坐标,在touchEnded方法中我们获取手势结束时的坐标,接着判断是否符合我们的条件(即水平向左滑动且上下滑动不超过10),如果符合切换视图。

 

接着同样我们要在SecondViewController.m中实现上述两个方法只不过判断条件不同如下:

 


即水平向右滑动,切换原来的视图。编译运行,如果不出意外的话,会出现下面的效果:

 

向左滑动

 

向右滑动


 

好了就写这么多,有什么问题请留言,大家一起学习交流!





转载于:https://www.cnblogs.com/jackljf/archive/2012/09/11/3589303.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/262434.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Office Word界面和页面字体模糊不清的解决方案

最近在打开Word编辑文档时&#xff0c;发现了一个非常让人懊恼的现象&#xff0c;整个Word的界面和页面中的字体变得模糊不清&#xff0c;光标所到之处会短暂地局部变清晰&#xff0c;完全没法正常使用。 觉得很奇怪&#xff0c;以前一直能正常使用&#xff0c;为什么现在就出…

PS自动生成不同尺寸图标

2019独角兽企业重金招聘Python工程师标准>>> ####一、简介 通过在ps执行脚本将当前编辑的素材生成各种不同尺寸的png图标。下面例子在cs6下测试通过。 参考&#xff1a;http://coolketang.com/psscript/menu8lesson12.php ####二、脚本jsx //调用[File]的[openDialo…

如何在Word中输入带圈数字1-10的黑底白字和白底黑字的数字字符?

当需要在文档中输入带圈的数字字符时&#xff0c;你知道如何输入吗&#xff1f; 通常的输入法软键盘中会提供带圈的数字字符&#xff0c;但是都是白底黑字的。如果想输出黑底白字的带圈数字字符&#xff0c;该如何输入呢&#xff1f; 答案很简单&#xff1a; 在Word 2013中&…

Oracle存储过程 使用游标、数组的配合查询

查询输入的门牌号码是否在标准门牌库中存在。存在则返回相应的号码。 1 public string GetValidate&#xff08;&#xff09;{2 3 OracleConnection conn ConnectOra();4 string retRepMphm;5 try6 {7 conn.open();8 OracleComandType cmdconn.CreateCommand();9 cmd.Com…

Windows环境下Code::Blocks中成功配置MySQL Connector/C连接MySQL数据库

下面我将介绍当需要用C语言开发能访问MySQL数据库的程序时成功配置的关键事项。 操作系统&#xff1a;Windows 7 x64 编程环境&#xff1a;Code::Blocks 10.05(32bit) 配置方式说明&#xff1a;用C语言开发访问MySQL数据库至少有3种配置方式。 方式1&#xff1a;使用MySQL C…

离散卷积过程举例图示详解

卷积结果的通俗解释&#xff1a; f(x)是待处理的信号。通常地&#xff0c;该函数数据个数比较多。 g(x)是对信号前后相关性的描述的函数。通常地&#xff0c;该函数非零数据个数比较少。它通常具有在(-∞,∞)内可积且快速收敛的特性。 对于本例中的函数g(x)&#xff0c;卷积…

c语言数据结构将链串里所有值为x的字符删除_redis数据结构与对象到底长什么样?...

写在前面前方高能&#xff01;前方高能&#xff01;前方高能&#xff01;文章较长&#xff0c;可能需要花费您两个小时的时间&#xff0c;请做好心理准备&#xff0c;但是一旦你准备看下去&#xff0c;我相信您一定会有收获&#xff0c;不枉此行&#xff0c;let’s go!!!一、简…

java中判断字段真实长度(中文2个字符,英文1个字符)的方法

public class Char_cn { public static void main(String[] args) { // TODO Auto-generated method stub String haha "我叫兜兜abcd"; int true_num String_length(haha); System.out.println("true" true_num); int false_num haha.length();…

mysql主从技术_MySQL主从架构的实现

目录一主一从1.1 环境准备&#xff1a;1.2 准备步骤&#xff1a;1.3 实现步骤:1.3.1 配置master主服务器1.3.2 创建复制帐号1.3.3 查看主服务器状态1.3.4 配置slave从服务器1.3.5 启动从服务器复制线程1.3.6 查看从服务器状态1.3.7 测试1.4 扩展——实现一主多从1.4.1 需求分析…

C# winfrom listView

转自http://hi.baidu.com/gaoisbest/item/84034943d4d94195823ae12b 1.如何为listview手动添加第一列和第二列数据 for (int i 0; i < 10; i) { ListViewItem Item new ListViewItem(); Item.Text dt.Rows[i][1].ToString();//读取数据库中字段 Item.Tag dt.…

[教程指导]索尼官方4.0.3系统一键root方法! [复制链接]

[教程指导]索尼官方4.0.3系统一键root方法&#xff01; [复制链接] 青青子衿198976青青子衿198976注册时间2011-9-26最后登录2012-9-15在线时间730 小时阅读权限150积分3996帖子1173精华32UID13306151版主 在线时间730 小时经验1629 分贡献2693 点极币10442 极币最后登录2012-9…

python --函数

转http://www.cnblogs.com/vamei/archive/2012/06/01/2529500.html 函数最重要的目的是方便我们重复使用相同的一段程序。 将一些操作隶属于一个函数&#xff0c;以后你想实现相同的操作的时候&#xff0c;只用调用函数名就可以&#xff0c;而不需要重复敲所有的语句。 函数的定…

国外网站设计欣赏:30个优秀的国外咖啡网站设计

梦想天空博客向大家分享各种类型的优秀网站作品&#xff0c;有电子商务网站设计、旅游网站设计、餐馆网站设计、设计工作室网站设计、大学网站设计以及各种色系&#xff0c;各种风格的网站设计作品。今天这篇文章给大家带来30个优秀的国外咖啡网站设计作品&#xff0c;一起欣赏…

【译】CodeIgniter HMVC模块扩展使用文档

CodeIgniter HMVC扩展说明 原文地址&#xff1a;Modular Extensions - HMVC 模块扩展——HMVC 模块扩展让CodeIgniter框架模块化。模块是一组独立的组件&#xff08;通常有模型、控制器和视图&#xff09;&#xff0c;它们被分类在应用模块的子文件夹中&#xff0c;并且能够直接…

python函数参数类型及顺序_python函数参数类型及其顺序

根据inspect模块官文文档中关于函数参数类型的相关说明&#xff0c;python函数参数共有五种类型&#xff0c;按顺序分别为&#xff1a;POSITIONAL_ONLY、POSITIONAL_OR_KEYWORD、VAR_POSITIONAL、KEYWORD_ONLY、VAR_KEYWORD。如图&#xff1a;POSITIONAL_ONLY&#xff1a;参数值…

JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)

原文:JS组件系列——Bootstrap Table 表格行拖拽&#xff08;二&#xff1a;多行拖拽&#xff09;前言&#xff1a;前天刚写了篇JS组件系列——Bootstrap Table 表格行拖拽&#xff0c;今天接到新的需要&#xff0c;需要在之前表格行拖拽的基础上能够同时拖拽选中的多行。博主用…

scrapy知乎爬虫mysql存储项目_Scrapy爬虫框架第八讲【项目实战篇:知乎用户信息抓取】--本文参考静觅博主所写...

思路分析&#xff1a;(1)选定起始人(即选择关注数和粉丝数较多的人--大V)(2)获取该大V的个人信息(3)获取关注列表用户信息(4)获取粉丝列表用户信息(5)重复(2)(3)(4)步实现全知乎用户爬取实战演练&#xff1a;(1)、创建项目&#xff1a;scrapy startproject zhijutest(2)、创建爬…

kali下生成web端后门

很多时候在***测试时选择web***害怕用的别人的马带有后门&#xff0c;这样自己的辛苦就要被别人不劳而获&#xff0c;很多时候我们都想拥有自己的马&#xff0c;那么这个时候你就应该使用kail来生成一个自己独特密码的web***了。Kali Linux自带有好几个web***生成工具&#xff…

素性测试的Miller-Rabin算法完全解析 (C语言实现、Python实现)

因为文中存在公式&#xff0c;只能用图片方式上传了&#xff01; 以下为C语言源代码&#xff1a; #include <stdio.h> typedef long long unsigned LLU; typedef int BOOL; #define TRUE 1 #define FALSE 0 BOOL isPrime(LLU n) { //这是传统的方法&#xff0c;用于与…

MongoDB源码阅读之ReplSet源码分析

1. ReplSet源码结构 rs_config.h replSet间同步设置的工具类 rs_member.h 心跳检测类和replSet成员状态的定义 rs_sync.h 同步数据类 rs.h 定义了几乎所有replSet相关的类&#xff08;Member:replSet中的节点成员&#xff0c; GhostSync&#xff1a;备份同步类&#xff0c;Rep…