新的iPhone6s和iPhone6s Plus新添加了3D Touch的功能,本文简单介绍一下如何在Xamarin.iOS上面使用该功能。
3D Touch不仅会感知用户按压屏幕,也会感知压力的大小。要注意一点的是,现在的iOS模拟器并不支持3D Touch,大家必须要在iPhone 6s/6s Plus 真机上进行测试和调试。
3D Touch可以给你的应用带来全新的交互方式.
- Pressure Sensitivity - 应用可以感知用户按压屏幕的压力。这样一些绘画应用可以根据压力的大小改变笔触。
- Peek and Pop - 应用可在单一页面中获取更多的内容。用户用力按压屏幕会弹出当前条目的额外信息,比如一些预览信息,这个行为叫做Peek,当用户再用力一点,可以跳转到预览信息的页面,这个行为叫做Pop。
- Quick Actions - 这个行为有点类似于windows中的右键菜单,但是只是针对于应用图标的,显示一些关于这个应用的额外选项
下面我们分开来讲
Pressure Sensitivity
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
...
}
}
这里要注意的是,用户按压会触发TouchesMoved的事件,在这样的情况下X/Y的值是不变的,如果你的应用之前的代码是通过这个事件来判断X,Y值的是否改变,现在需要注意,X/Y不一定改变。相关文档请参考 TouchCanvas: Using UITouch efficiently and effectively 和 UITouch Class Reference.
Peek and Pop
这个交互行为会让用户更快的获取信息,比如用户在浏览一个表格,用户可以按压表格中的某项,获取一些关于该项的概况信息(这个行为叫做Peek),再用力一些,就可进入该项的详情页(这个行为叫做Pop或Pop-ping)。
检测设备是否支持3D Touch
可以通过下面的代码,在UIViewController 中判断当前设备是否支持3D Touch
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// 检测是否支持3D Touch
if (TraitCollection.ForceTouchCapability == UIForceTouchCapability.Available) {
// 设备支持
...
}
}
响应Peek和Pop行为
我们可以通过继承UIViewControllerPreviewingDelegate类来响应Peek和Pop的行为。请参考下面代码,假设我们之前提到的表格叫做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
/// 继续按压触发Pop事件
public override void CommitViewController (IUIViewControllerPreviewing previewingContext, UIViewController viewControllerToCommit)
{
// 直接使用之前创建好的详情页面
MasterController.ShowViewController(viewControllerToCommit,this);
}
/// 创建预览页面,当用户触发Peek事件
public override UIViewController GetViewControllerForPreview (IUIViewControllerPreviewing previewingContext, CGPoint location)
{
// 判断表格中的条目
var indexPath = MasterController.TableView.IndexPathForRowAtPoint (location);
var cell = MasterController.TableView.CellAt (indexPath);
var item = MasterController.dataSource.Objects [indexPath.Row];
// 创建ViewController,并设置初始位置
var detailViewController = MasterController.Storyboard.InstantiateViewController ("DetailViewController") as DetailViewController;
detailViewController.PreferredContentSize = new CGSize (0, 0);
// 填入数据
detailViewController.SetDetailItem (item);
detailViewController.NavigationItem.LeftBarButtonItem = MasterController.SplitViewController.DisplayModeButtonItem;
detailViewController.NavigationItem.LeftItemsSupplementBackButton = true;
// 设置预览页面的位置,模糊其他页面
previewingContext.SourceRect = cell.Frame;
return detailViewController;
}
#endregion
}
}
代码中GetViewControllerForPreview函数用来响应Peek行为,在这个函数中,首先我们获取当前表单,然后我们加载DetailViewController,接着通过PreferredContentSize设置Peek窗口的默认大小,最后我们通过previewingContext.SourceRect = cell.Frame 这段代码来模糊其他表单,然后返回我们想要的窗口。
CommitViewController 这个函数会利用我们在Peek行为中创建的窗口,来给Pop显示。
注册Peek和Pop行为
在使用Peek和Pop之前,我们要注册他们,在当前的ViewController,请参考下面代码
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// 判断设备是否支持3D Touch
if (TraitCollection.ForceTouchCapability == UIForceTouchCapability.Available) {
// 注册Pop和Peek
RegisterForPreviewingWithDelegate(new PreviewingDelegate(this), View);
}
...
}
在这里,我们调用RegisterForPreviewingWithDelegate方法把创建的PreviewingDelegate实例传进去,更多信息请参考
iOS 9 ApplicationShortcuts Sample , ViewControllerPreviews: Using the UIViewController previewing APIs , UIPreviewAction Class Reference, UIPreviewActionGroup Class Reference 和 UIPreviewActionItem Protocol Reference.
Quick Actions
使用这个功能,可以让用户从应用的图标上,更快捷和直接地调用应用内的函数,我们可以把这个功能理解为桌面程序的右键菜单定义静态Quick Actions
Quick Actions的行为可以是一个或者多个,我们需要在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>
下面是Quick Actions可以定义的几个键
- UIApplicationShortcutItemIconType 这个键值用于Quick Actions的图标,可以是以下这几个值
- UIApplicationShortcutIconTypeCompose
- UIApplicationShortcutIconTypePlay
- UIApplicationShortcutIconTypePause
- UIApplicationShortcutIconTypeAdd
- UIApplicationShortcutIconTypeLocation
- UIApplicationShortcutIconTypeSearch
- UIApplicationShortcutIconTypeShare
- UIApplicationShortcutItemSubtitle 可以定义副标题
- UIApplicationShortcutItemTitle 定义标题
- UIApplicationShortcutItemType 这个值会被代码中引用
定义Quick Action的条目
我们可以通过Info.plist定义Quick Action,然后通过UIApplicationShortcutItemType定义接入点,来确定用户点击了哪个条目,并与之交互
为了更方便的使用这些接入点,我们也在代码中定义,如下
为了更方便的使用这些接入点,我们也在代码中定义,如下
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";
}
}
处理Quick Action事件
接下来,我们要修改AppDelegate.cs中的代码,去响应用户选择的Quick Action事件,代码如下
using System;
...
public UIApplicationShortcutItem LaunchedShortcutItem { get; set; }
public bool HandleShortcutItem(UIApplicationShortcutItem shortcutItem) {
var handled = false;
if (shortcutItem == null) return false;
// 根据Shortcut的定义来执行相应函数
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 handled;
}
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
var shouldPerformAdditionalDelegateHandling = true;
// 获取shortcut条目
if (launchOptions != null) {
LaunchedShortcutItem = launchOptions [UIApplication.LaunchOptionsShortcutItemKey] as UIApplicationShortcutItem;
shouldPerformAdditionalDelegateHandling = (LaunchedShortcutItem == null);
}
return shouldPerformAdditionalDelegateHandling;
}
public override void OnActivated (UIApplication application)
{
// 处理shortcut 被选中
HandleShortcutItem(LaunchedShortcutItem);
// 设置为空
LaunchedShortcutItem = null;
}
public override void PerformActionForShortcutItem (UIApplication application, UIApplicationShortcutItem shortcutItem, UIOperationHandler completionHandler)
{
completionHandler(HandleShortcutItem(shortcutItem));
}
首先,我们定义LaunchedShortcutItem属性来记录用户选择的Quick Action,接着我们重载FinishedLaunching方法,通过launchOptions参数来获取是否用户选择了Quick Action。
我们重载OnActivated函数,在这里,我们可以处理用户的Quick Action事件,上面代码中,我们只是打印消息,但在实际的应用中,我们可以在这里加入响应的代码。在处理Quick Action之后,我们设置LaunchedShortcutItem属性为空。
最后,如果你的应用已经在运行,如果用户再触发Quick Action,这个时候PerformActionForShortcutItem函数会被响应,同样,我们也要重载这个函数,处理Quick Action事件。
创建动态Quick Action条目
除了我们在Info.plist中创建静态Quick Action条目以外,我们可以通过代码动态添加Quick Action条目,我们只需修改一下FinishedLaunching,代码如下public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
var shouldPerformAdditionalDelegateHandling = true;
if (launchOptions != null) {
LaunchedShortcutItem = launchOptions [UIApplication.LaunchOptionsShortcutItemKey] as UIApplicationShortcutItem;
shouldPerformAdditionalDelegateHandling = (LaunchedShortcutItem == null);
}
// 添加动态条目
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)
};
// 更新应用中的动态条目
application.ShortcutItems = new UIApplicationShortcutItem[]{shortcut3, shortcut4};
}
return shouldPerformAdditionalDelegateHandling;
}
首先,我们要确定application是否已经添加了动态的Quick Action条目,如果没有,我们就创建两个,给ShortcutItems属性赋值。
剩下处理Quick Action的代码跟上一节添加静态的Quick Action的一样。
我们既可以添加动态的也可以添加静态的。更多信息可以参考
iOS 9 ViewControllerPreview Sample , ApplicationShortcuts: Using UIApplicationShortcutItem , UIApplicationShortcutItem Class Reference, UIMutableApplicationShortcutItem Class Reference 和 UIApplicationShortcutIcon Class Reference.