Xamarin.iOS 中的触摸事件和手势

了解 iOS 应用程序中的触摸事件和触摸 API 非常重要,因为它们是与设备的所有物理交互的核心。 所有触摸交互都涉及 对象 UITouch 。 本文介绍如何使用 UITouch 类及其 API 来支持触摸。 稍后,我们将扩展我们的知识,了解如何支持手势。

启用触控

中的 UIKit 控件(从 UIControl 子类)非常依赖于用户交互,因此 UIKit 中内置了手势,因此无需启用 Touch。 它已启用。

但是,中的 UIKit 许多视图默认未启用触摸。 可通过两种方法在控件上启用触摸。 第一种方法是在 iOS Designer的属性板中检查“已启用用户交互”复选框,如以下屏幕截图所示:

选中 iOS Designer属性板中的“启用用户交互”复选框

我们还可以使用控制器在类上UIView将 属性设置为 UserInteractionEnabled true。 如果在代码中创建 UI,则这是必需的。

以下代码行是一个示例:

imgTouchMe.UserInteractionEnabled = true;

触摸事件

当用户触摸屏幕、移动手指或移除手指时,会发生三个阶段的触摸。 这些方法在 中 UIResponder定义,它是 UIView 的基类。 iOS 将替代 和 UIViewController 上的UIView关联方法,以处理触摸:

  • TouchesBegan – 首次触摸屏幕时会调用此名称。
  • TouchesMoved – 当用户在屏幕上滑动手指时触摸位置发生更改时,将调用此名称。
  • TouchesEndedTouchesCancelled - TouchesEnded 在用户手指从屏幕中抬出时调用 。 TouchesCancelled 如果 iOS 取消触摸,则调用 ,例如,如果用户将手指从按钮上滑动以取消按下。

触摸事件在 UIView 堆栈中以递归方式向下传播,以检查触摸事件是否在视图对象的边界内。 这通常称为 命中测试。 它们首先在最UIView顶部调用,然后在UIViewControllerUIView视图层次结构中的 和 UIViewControllers 下调用它们。

每次用户触摸屏幕时,都会创建一个 UITouch 对象。 对象 UITouch 包括有关触摸的数据,例如触摸发生时间、发生位置、触摸是否为轻扫等。触摸事件将传递一个 touchs 属性 - 一个 NSSet 包含一个或多个触摸的 。 可以使用此属性来获取对触摸的引用,并确定应用程序的响应。

替代其中一个触摸事件的类应首先调用基本实现,然后获取与 UITouch 事件关联的 对象。 若要获取对第一次触摸的引用,请调用 AnyObject 属性并将其强制转换为 , UITouch 如以下示例所示:

public override void TouchesBegan (NSSet touches, UIEvent evt)
{
    base.TouchesBegan (touches, evt);
    UITouch touch = touches.AnyObject as UITouch;
    if (touch != null)
    {
        //code here to handle touch
    }
}

iOS 自动识别屏幕上连续的快速触摸,并在单个 UITouch 对象中一键收集它们。 这使得检查双击就像检查 TapCount 属性一样简单,如以下代码所示:

public override void TouchesBegan (NSSet touches, UIEvent evt)
{
    base.TouchesBegan (touches, evt);
    UITouch touch = touches.AnyObject as UITouch;
    if (touch != null)
    {
        if (touch.TapCount == 2)
        {
            // do something with the double touch.
        }
    }
}

多点触控

默认情况下,控件上不启用多点触控。 可以在 iOS Designer中启用多点触控,如以下屏幕截图所示:

在 iOS Designer中启用了多点触控

还可以通过设置 MultipleTouchEnabled 属性以编程方式设置多点触控,如以下代码行所示:

imgTouchMe.MultipleTouchEnabled = true;

若要确定触摸屏幕的手指数,请使用 Count 属性上的 UITouch 属性:

public override void TouchesBegan (NSSet touches, UIEvent evt)
{
    base.TouchesBegan (touches, evt);
    lblNumberOfFingers.Text = "Number of fingers: " + touches.Count.ToString();
}

确定触摸位置

方法 UITouch.LocationInView 返回一个 CGPoint 对象,该对象保存给定视图中触摸的坐标。 此外,我们还可以通过调用 方法 Frame.Contains来测试该位置是否在控件中。 以下代码片段演示了一个示例:

if (this.imgTouchMe.Frame.Contains (touch.LocationInView (this.View)))
{
    // the touch event happened inside the UIView imgTouchMe.
}

现在,我们已经了解了 iOS 中的触摸事件,接下来我们来了解手势识别器。

手势识别器

手势识别器可以极大地简化和减少编程工作量,以支持应用程序中的触摸。 iOS 手势识别器将一系列触摸事件聚合为单个触摸事件。

Xamarin.iOS 将 类 UIGestureRecognizer 作为以下内置手势识别器的基类提供:

  • UITapGestureRecognizer – 这适用于一个或多个点击。
  • UIPinchGestureRecognizer - 捏合和分散手指。
  • UIPanGestureRecognizer – 平移或拖动。
  • UISwipeGestureRecognizer – 在任意方向轻扫。
  • UIRotationGestureRecognizer - 以顺时针或逆时针运动旋转两根手指。
  • UILongPressGestureRecognizer – 长按,有时也称为长按或长按。

使用手势识别器的基本模式如下所示:

  1. 实例化手势识别器 – 首先,实例化 UIGestureRecognizer 子类。 实例化的对象将由视图关联,并在处理视图时进行垃圾回收。 无需将此视图创建为类级变量。
  2. 配置任何手势设置 - 下一步是配置手势识别器。 有关可设置为控制实例行为的UIGestureRecognizer属性列表,请参阅 Xamarin 及其子类的文档UIGestureRecognizer
  3. 配置目标 - 由于其 Objective-C 传统,当手势识别器与手势匹配时,Xamarin.iOS 不会引发事件。 UIGestureRecognizer 有一个 方法 – AddTarget – 可以接受匿名委托或 Objective-C 选择器,其中包含在手势识别器进行匹配时要执行的代码。
  4. 启用手势识别器 – 与触摸事件一样,仅当启用触摸交互时,才会识别手势。
  5. 将手势识别器添加到视图 – 最后一步是通过调用 View.AddGestureRecognizer ,并向其传递手势识别器对象,将手势添加到视图。

有关如何在代码中实现手势 识别器示例的详细信息,请参阅手势识别器示例

调用手势的目标时,将向它传递对所发生的手势的引用。 这允许手势目标获取有关所发生的手势的信息。 可用信息的范围取决于使用的手势识别器的类型。 有关每个 UIGestureRecognizer 子类可用的数据的信息,请参阅 Xamarin 的文档。

请务必记住,一旦将手势识别器添加到视图中,视图 (,其下方) 的任何视图都不会收到任何触摸事件。 若要允许使用手势同时发生触摸事件, CancelsTouchesInView 必须将 属性设置为 false,如以下代码所示:

_tapGesture.Recognizer.CancelsTouchesInView = false;

每个都有 UIGestureRecognizer 一个 State 属性,该属性提供有关手势识别器状态的重要信息。 每当此属性的值更改时,iOS 都会调用订阅方法,为它提供更新。 如果自定义手势识别器从不更新 State 属性,则永远不会调用订阅服务器,从而使手势识别器无用。

手势可以汇总为以下两种类型之一:

  1. 离散 - 这些手势仅在首次识别时触发。
  2. 连续 – 只要这些手势被识别,这些手势就会继续触发。

手势识别器存在于以下状态之一:

  • 可能 - 这是所有手势识别器的初始状态。 这是 State 属性的默认值。
  • 开始 – 首次识别连续手势时,状态设置为“开始”。 这允许订阅者区分手势识别开始时间和更改时间。
  • 已更改 – 在连续手势开始但尚未完成之后,每次移动或更改触摸时,状态都将设置为“更改”,只要它仍在手势的预期参数内。
  • 已取消 – 如果识别器从“开始”转到“已更改”,然后触摸以不再适合手势的模式的方式更改,则将设置此状态。
  • 已识别 - 当手势识别器与一组触摸匹配时,将设置状态,并将通知订阅者手势已完成。
  • 已结束 - 这是“已识别”状态的别名。
  • 失败 – 当手势识别器无法再匹配它正在侦听的触摸时,状态将更改为“失败”。

Xamarin.iOS 表示枚举中的 UIGestureRecognizerState 这些值。

使用多个手势

默认情况下,iOS 不允许默认手势同时运行。 相反,每个手势识别器将按非确定性顺序接收触摸事件。 以下代码片段演示了如何使手势识别器同时运行:

gesture.ShouldRecognizeSimultaneously += (UIGestureRecognizer r) => { return true; };

还可以在 iOS 中禁用手势。 有两个委托属性允许手势识别器检查应用程序的状态和当前触摸事件,从而决定如何以及是否应识别手势。 这两个事件是:

  1. ShouldReceiveTouch – 在手势识别器传递触摸事件之前调用此委托,并提供一个机会来检查触摸并确定手势识别器将处理哪些触摸。
  2. ShouldBegin – 当识别器尝试将状态从“可能”更改为某个其他状态时调用。 返回 false 将强制将手势识别器的状态更改为“失败”。

可以使用强类型 UIGestureRecognizerDelegate、弱委托或通过事件处理程序语法绑定来替代这些方法,如以下代码片段所示:

gesture.ShouldReceiveTouch += (UIGestureRecognizer r, UITouch t) => { return true; };

最后,可以将手势识别器排队,以便仅在另一个手势识别器失败时才会成功。 例如,仅当双击手势识别器失败时,一次点击手势识别器才应成功。 以下代码片段提供了一个示例:

singleTapGesture.RequireGestureRecognizerToFail(doubleTapGesture);

创建自定义手势

尽管 iOS 提供一些默认手势识别器,但在某些情况下可能需要创建自定义手势识别器。 创建自定义手势识别器涉及以下步骤:

  1. 子类 UIGestureRecognizer
  2. 重写相应的触摸事件方法。
  3. 通过基类的 State 属性弹出识别状态。

iOS 中使用触控 演练中将介绍此操作的一个实际示例。