Xamarin.iOS 中的 3D Touch 简介

本文介绍如何在应用中使用新的 iPhone 6s 和 iPhone 6s Plus 3D Touch 手势。

已启用 3D Touch 的应用示例

本文介绍如何使用新的 3D Touch API 为在新 iPhone 6s 和 iPhone 6s Plus 设备上运行的 Xamarin.iOS 应用添加压力敏感手势。

借助 3D Touch,iPhone 应用现在不仅能够判断用户是否在触摸设备屏幕,还可以感知用户施加的压力并对不同的压力级别做出响应。

3D Touch 为应用提供以下功能:

  • 压力感应 - 应用现在可以测量用户触摸屏幕的力度并利用该信息。 例如,绘画应用可以根据用户触摸屏幕的力度来使线条变粗或变细。
  • 轻瞄和突显 - 应用现在可以让用户与其数据交互,而无需离开当前上下文。 通过用力按压屏幕,他们可以使用速览来预览感兴趣的项(例如预览消息)。 如果再用力按压,则可使用弹出来展开该项。
  • 快速操作 - 将快速操作想象为上下文菜单,当用户右键单击桌面应用中的项时,可以弹出这些菜单。 使用快速操作,可以直接从主屏幕上的应用图标为应用中的功能添加快捷方式。
  • 在模拟器中测试 3D Touch - 使用正确的 Mac 硬件,可以在 iOS 模拟器中测试已启用 3D Touch 的应用。

压力敏感度

如上所述,通过使用 UITouch 类的新属性,可以测量用户施加在 iOS 设备屏幕上的压力,并在用户界面中使用此信息。 例如,根据压力大小使画笔笔划变得更加透明或更不透明。

根据压力大小渲染为更透明或不透明的画笔笔划

得益于 3D Touch,如果你的应用在 iOS 9(或更高版本)上运行,并且 iOS 设备能够支持 3D Touch,则压力的变化将导致引发 TouchesMoved 事件。

例如,在监视 UIViewTouchesMoved 事件时,可以使用以下代码获取用户施加在屏幕上的当前压力:

public override void TouchesMoved (NSSet touches, UIEvent evt)
{
    base.TouchesMoved (touches, evt);
    UITouch touch = touches.AnyObject as UITouch;
    if (touch != null)
    {
        // Get the pressure
        var force = touch.Force;
        var maxForce = touch.MaximumPossibleForce;

        // Do something with the touch and the pressure
        ...
    }
}

MaximumPossibleForce 属性根据运行应用的 iOS 设备返回 UITouchForce 属性的最高可能值。

重要

即使 X/Y 坐标未更改,压力的变化也会引发 TouchesMoved 事件。 由于这种行为更改,你的 iOS 应用应为更频繁地调用 TouchesMoved 事件做好准备,同时为 X/Y 坐标与上次 TouchesMoved 调用相同的情况做好准备。

有关详细信息,请参阅 Apple 的 TouchCanvas:高效使用 UITouch 示例应用和 UITouch 类引用

速览和弹出

3D Touch 为用户提供比以往更快地与应用中的信息交互的新方法,而无需从其当前位置导航。

例如,如果你的应用显示一个消息表,则用户可以用力按压某个项,以在覆盖视图中预览其内容(Apple 称之为“速览”)。

“查看内容”的示例

如果用户更用力地按压,他们将进入常规消息视图(这称为弹出展开视图)。

检查 3D Touch 可用性

使用 UIViewController 时,可以使用以下代码查看运行应用的 iOS 设备是否支持 3D Touch:

public override void TraitCollectionDidChange(UITraitCollection previousTraitCollection)
{
    //Important: call the base function
    base.TraitCollectionDidChange(previousTraitCollection);

    //See if the new TraitCollection value includes force touch
    if (TraitCollection.ForceTouchCapability == UIForceTouchCapability.Available) {
        //Do something with 3D touch, for instance...
        RegisterForPreviewingWithDelegate (this, View);
        ...

此方法可以在之前或之后ViewDidLoad()调用。

处理速览和弹出

在可处理 3D Touch 的 iOS 设备上,可以使用 UIViewControllerPreviewingDelegate 类的实例来处理速览和弹出项详细信息的显示。 例如,如果我们有一个被称为 MasterViewController 的表视图控制器,则可以使用以下代码来支持速览和弹出

using System;
using System.Collections.Generic;
using UIKit;
using Foundation;
using CoreGraphics;

namespace DTouch
{
    public class PreviewingDelegate : UIViewControllerPreviewingDelegate
    {
        #region Computed Properties
        public MasterViewController MasterController { get; set; }
        #endregion

        #region Constructors
        public PreviewingDelegate (MasterViewController masterController)
        {
            // Initialize
            this.MasterController = masterController;
        }

        public PreviewingDelegate (NSObjectFlag t) : base(t)
        {
        }

        public PreviewingDelegate (IntPtr handle) : base (handle)
        {
        }
        #endregion

        #region Override Methods
        /// Present the view controller for the "Pop" action.
        public override void CommitViewController (IUIViewControllerPreviewing previewingContext, UIViewController viewControllerToCommit)
        {
            // Reuse Peek view controller for details presentation
            MasterController.ShowViewController(viewControllerToCommit,this);
        }

        /// Create a previewing view controller to be shown at "Peek".
        public override UIViewController GetViewControllerForPreview (IUIViewControllerPreviewing previewingContext, CGPoint location)
        {
            // Grab the item to preview
            var indexPath = MasterController.TableView.IndexPathForRowAtPoint (location);
            var cell = MasterController.TableView.CellAt (indexPath);
            var item = MasterController.dataSource.Objects [indexPath.Row];

            // Grab a controller and set it to the default sizes
            var detailViewController = MasterController.Storyboard.InstantiateViewController ("DetailViewController") as DetailViewController;
            detailViewController.PreferredContentSize = new CGSize (0, 0);

            // Set the data for the display
            detailViewController.SetDetailItem (item);
            detailViewController.NavigationItem.LeftBarButtonItem = MasterController.SplitViewController.DisplayModeButtonItem;
            detailViewController.NavigationItem.LeftItemsSupplementBackButton = true;

            // Set the source rect to the cell frame, so everything else is blurred.
            previewingContext.SourceRect = cell.Frame;

            return detailViewController;
        }
        #endregion
    }
}

GetViewControllerForPreview 方法用于执行速览操作。 它获取对表单元格和支持数据的访问权限,然后从当前情节提要加载 DetailViewController。 将 PreferredContentSize 设置为 (0,0),即表示要求使用默认的速览视图大小。 最后,我们用 previewingContext.SourceRect = cell.Frame 模糊显示除我们当前显示的单元格以外的所有内容,并返回用于显示的新视图。

当用户更用力地按压时,CommitViewController 会将我们在速览中创建的视图重用于弹出视图。

注册速览和弹出

在我们希望允许用户从中速览和弹出项的视图控制器中,我们需要注册此服务。 在表视图控制器的上述示例中 (MasterViewController),我们将使用以下代码:

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();

    // Check to see if 3D Touch is available
    if (TraitCollection.ForceTouchCapability == UIForceTouchCapability.Available) {
        // Register for Peek and Pop
        RegisterForPreviewingWithDelegate(new PreviewingDelegate(this), View);
    }
    ...

}

在这里,我们将使用上面创建的 PreviewingDelegate 的实例调用 RegisterForPreviewingWithDelegate 方法。 在支持 3D Touch 的 iOS 设备上,用户可以用力按压某个项来速览它。 如果他们更用力地按压,该项将弹出展开到标准显示视图中。

有关详细信息,请参阅 Apple 的 ViewControllerPreviews:使用 UIViewController 预览 API 示例应用、UIPreviewAction 类引用UIPreviewActionGroup 类引用UIPreviewActionItem 协议引用

快速操作

使用 3D Touch 和快速操作,可以从 iOS 设备上的主屏幕图标为应用中的功能添加常用、快速且易于访问的快捷方式。

如上所述,你可以将快速操作想象为上下文菜单,当用户右键单击桌面应用中的项时,就可以弹出这些菜单。 应使用快速操作来提供应用的常见功能的快捷方式。

“快速操作”菜单的示例

定义静态快速操作

如果应用所需的一个或多个快速操作是静态的,并且不需要更改,则可以在应用的 Info.plist 文件中定义它们。 在外部编辑器中编辑此文件并添加以下注册表项:

<key>UIApplicationShortcutItems</key>
<array>
    <dict>
        <key>UIApplicationShortcutItemIconType</key>
        <string>UIApplicationShortcutIconTypeSearch</string>
        <key>UIApplicationShortcutItemSubtitle</key>
        <string>Will search for an item</string>
        <key>UIApplicationShortcutItemTitle</key>
        <string>Search</string>
        <key>UIApplicationShortcutItemType</key>
        <string>com.company.appname.000</string>
    </dict>
    <dict>
        <key>UIApplicationShortcutItemIconType</key>
        <string>UIApplicationShortcutIconTypeShare</string>
        <key>UIApplicationShortcutItemSubtitle</key>
        <string>Will share an item</string>
        <key>UIApplicationShortcutItemTitle</key>
        <string>Share</string>
        <key>UIApplicationShortcutItemType</key>
        <string>com.company.appname.001</string>
    </dict>
</array>

下面我们将使用以下注册表项来定义两个静态快速操作项:

  • UIApplicationShortcutItemIconType - 定义“快速操作”项将显示为以下值之一的图标:

    • UIApplicationShortcutIconTypeAdd
    • UIApplicationShortcutIconTypeAlarm
    • UIApplicationShortcutIconTypeAudio
    • UIApplicationShortcutIconTypeBookmark
    • UIApplicationShortcutIconTypeCapturePhoto
    • UIApplicationShortcutIconTypeCaptureVideo
    • UIApplicationShortcutIconTypeCloud
    • UIApplicationShortcutIconTypeCompose
    • UIApplicationShortcutIconTypeConfirmation
    • UIApplicationShortcutIconTypeContact
    • UIApplicationShortcutIconTypeDate
    • UIApplicationShortcutIconTypeFavorite
    • UIApplicationShortcutIconTypeHome
    • UIApplicationShortcutIconTypeInvitation
    • UIApplicationShortcutIconTypeLocation
    • UIApplicationShortcutIconTypeLove
    • UIApplicationShortcutIconTypeMail
    • UIApplicationShortcutIconTypeMarkLocation
    • UIApplicationShortcutIconTypeMessage
    • UIApplicationShortcutIconTypePause
    • UIApplicationShortcutIconTypePlay
    • UIApplicationShortcutIconTypeProhibit
    • UIApplicationShortcutIconTypeSearch
    • UIApplicationShortcutIconTypeShare
    • UIApplicationShortcutIconTypeShuffle
    • UIApplicationShortcutIconTypeTask
    • UIApplicationShortcutIconTypeTaskCompleted
    • UIApplicationShortcutIconTypeTime
    • UIApplicationShortcutIconTypeUpdate

    UIApplicationShortcutIconType 图像

  • UIApplicationShortcutItemSubtitle - 定义项的副标题。

  • UIApplicationShortcutItemTitle - 定义项的标题。

  • UIApplicationShortcutItemType - 用于标识应用中的项的字符串值。 有关详细信息,请参阅下一节。

重要

不能使用 Application.ShortcutItems 属性访问 Info.plist 文件中设置的快速操作快捷方式项。 它们仅传递到 HandleShortcutItem 事件处理程序。

标识快速操作项

如上所示,在应用的 Info.plist 中定义快速操作项时,就为 UIApplicationShortcutItemType 注册表项分配了一个字符串值来标识它们。

若要使这些标识符更易于在代码中使用,请将 ShortcutIdentifier 类添加到应用的项目中,使其如下所示:

using System;

namespace AppSearch
{
    public static class ShortcutIdentifier
    {
        public const string First = "com.company.appname.000";
        public const string Second = "com.company.appname.001";
        public const string Third = "com.company.appname.002";
        public const string Fourth = "com.company.appname.003";
    }
}

处理快速操作

接下来,需要修改应用的 AppDelegate.cs 文件,以处理用户在主屏幕上的应用图标中选择快速操作项的操作。

进行以下编辑:

using System;
...

public UIApplicationShortcutItem LaunchedShortcutItem { get; set; }

public bool HandleShortcutItem(UIApplicationShortcutItem shortcutItem) {
    var handled = false;

    // Anything to process?
    if (shortcutItem == null) return false;

    // Take action based on the shortcut type
    switch (shortcutItem.Type) {
    case ShortcutIdentifier.First:
        Console.WriteLine ("First shortcut selected");
        handled = true;
        break;
    case ShortcutIdentifier.Second:
        Console.WriteLine ("Second shortcut selected");
        handled = true;
        break;
    case ShortcutIdentifier.Third:
        Console.WriteLine ("Third shortcut selected");
        handled = true;
        break;
    case ShortcutIdentifier.Fourth:
        Console.WriteLine ("Forth shortcut selected");
        handled = true;
        break;
    }

    // Return results
    return handled;
}

public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
    var shouldPerformAdditionalDelegateHandling = true;

    // Get possible shortcut item
    if (launchOptions != null) {
        LaunchedShortcutItem = launchOptions [UIApplication.LaunchOptionsShortcutItemKey] as UIApplicationShortcutItem;
        shouldPerformAdditionalDelegateHandling = (LaunchedShortcutItem == null);
    }

    return shouldPerformAdditionalDelegateHandling;
}

public override void OnActivated (UIApplication application)
{
    // Handle any shortcut item being selected
    HandleShortcutItem(LaunchedShortcutItem);

    // Clear shortcut after it's been handled
    LaunchedShortcutItem = null;
}

public override void PerformActionForShortcutItem (UIApplication application, UIApplicationShortcutItem shortcutItem, UIOperationHandler completionHandler)
{
    // Perform action
    completionHandler(HandleShortcutItem(shortcutItem));
}

首先,我们定义一个公共 LaunchedShortcutItem 属性,用于跟踪用户上次选择的快速操作项。 然后,重写 FinishedLaunching 方法,并查看 launchOptions 是否已传递,以及它们是否包含快速操作项。 如果包含,我们会将快速操作存储在 LaunchedShortcutItem 属性中。

接下来,我们重写 OnActivated 方法,并将任何选定的快速启动项传递给要对其执行操作的 HandleShortcutItem 方法。 目前,我们只向控制台写入消息。 在实际应用中,你将处理所需的操作。 执行操作后,将清除 LaunchedShortcutItem 属性。

最后,如果应用已在运行,将调用 PerformActionForShortcutItem 方法来处理快速操作项,因此我们需要重写它并在此处调用 HandleShortcutItem 方法。

创建动态快速操作项

除了在应用的 Info.plist 文件中定义静态快速操作项之外,还可以创建动态实时快速操作。 若要定义两个新的动态快速操作,请再次编辑 AppDelegate.cs 文件并修改 FinishedLaunching 方法,使其如下所示:

public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
    var shouldPerformAdditionalDelegateHandling = true;

    // Get possible shortcut item
    if (launchOptions != null) {
        LaunchedShortcutItem = launchOptions [UIApplication.LaunchOptionsShortcutItemKey] as UIApplicationShortcutItem;
        shouldPerformAdditionalDelegateHandling = (LaunchedShortcutItem == null);
    }

    // Add dynamic shortcut items
    if (application.ShortcutItems.Length == 0) {
        var shortcut3 = new UIMutableApplicationShortcutItem (ShortcutIdentifier.Third, "Play") {
            LocalizedSubtitle = "Will play an item",
            Icon = UIApplicationShortcutIcon.FromType(UIApplicationShortcutIconType.Play)
        };

        var shortcut4 = new UIMutableApplicationShortcutItem (ShortcutIdentifier.Fourth, "Pause") {
            LocalizedSubtitle = "Will pause an item",
            Icon = UIApplicationShortcutIcon.FromType(UIApplicationShortcutIconType.Pause)
        };

        // Update the application providing the initial 'dynamic' shortcut items.
        application.ShortcutItems = new UIApplicationShortcutItem[]{shortcut3, shortcut4};
    }

    return shouldPerformAdditionalDelegateHandling;
}

现在,我们将检查 application 是否已包含一组动态创建的 ShortcutItems,如果不包含,我们将创建两个新的 UIMutableApplicationShortcutItem 对象来定义新项,并将其添加到 ShortcutItems 数组中。

在上面的处理快速操作部分中添加的代码将处理这些动态快速操作,就像处理静态操作一样。

应注意的是,可以混合创建静态和动态快速操作项(正如我们在此处执行的操作),而不必局限于某一种操作项。

有关详细信息,请参阅 Apple 的 ApplicationShortcuts:使用 UIApplicationShortcutItem 示例应用、UIApplicationShortcutItem 类引用UIMutableApplicationShortcutItem 类引用UIApplicationShortcutIcon 类引用

在模拟器中测试 3D Touch

在带有 Force Touch 启用触控板的兼容 Mac 上使用最新版本的 Xcode 和 iOS 模拟器时,可以在模拟器中测试 3D Touch 功能。

若要启用此功能,请在支持 3D Touch 的模拟 iPhone 硬件(iPhone 6s 和更新机型)中运行任何应用。 接下来,在 iOS 模拟器中选择“硬件”菜单,并启用“为 3D Touch 使用 Trackpad Force”菜单项:

在 iOS 模拟器中选择“硬件”菜单,并启用“为 3D Touch 使用 Trackpad Force”菜单项

启用此功能后,可以在 Mac 的触控板上更用力地按压以启用 3D Touch,就像在真正的 iPhone 硬件上操作一样。

总结

本文介绍了 iOS 9 中为 iPhone 6s 和 iPhone 6s Plus 提供的新 3D Touch API。 其中介绍了向应用添加压力敏感度;使用速览和弹出快速显示当前上下文中的应用内信息(无需导航);使用快速操作提供应用常用功能的快捷方式。