3D-Touch(1) iOS9 Quick Actions Shortcut

新的iPhone6s, iPhone6s P 在不久之前的 WWDC 上面发布了,这个版本的iPhone最大的卖点应该是在它的屏幕拥有了 3D Touch 的功能。iOS 9 中已经包含了这一硬件功能所提供API,3D Touch API分成三个部分: Quick Actions, peek and pop,以及 Pressure Sensitivity.考虑到苹果的一贯作风,如果你的APP中集成iPhone的新特性,新的API,被苹果商店推荐的概率也会增大一点。本文将为大家介绍如何快速的添加 Quick Actions shortcut 功能。

Home Screen Quick Actions

通过主屏幕的应用icon,可以用 3D Touch 呼出一个快捷列表,用户可通过这个列表快速定位应用功能模块。iOS9提供了两种屏幕标签,分别是静态标签和动态标签。且iOS9最多展示四个快捷键给用户,系统会优先展示静态的快捷键,当静态的快捷键不够四个,会添加动态的快捷键到列表。

静态快捷键的添加

打开 Info.plist, 在该文件中添加如下键值:

static_shortcut

添加一个key为UIApplicationShortcutItems的数组,数组中添件的元素就是静态标签,每个标签我们可以配置下面的键值:

  • UIApplicationShortcutItemType (required) : 快捷标签的唯一字符串标示
  • UIApplicationShortcutItemTitle (required): 快捷标签的标题,会显示在UI上
  • UIApplicationShortcutItemSubtitle (optional): 副标题,会显示在UI上
  • UIApplicationShortcutItemIconType (optional): 系统提供的icon,全部列表 UIApplicationShortcutIcon_Class
  • UIApplicationShortcutItemIconFile (optional): 自定义icon(如果填写了该项,则系统自动ignore UIApplicationShortcutItemIconType)。图片需要时正方形的,3535的倍数(试过100100也是Ok的),并且单色。
  • UIApplicationShortcutItemUserInfo (optional): 传值用

详情请看这里

当在Info.plist 中添加好了需要的标签之后。运行程序便可得到以下效果:

shortcut2

P.S 关于如何在模拟器中调试ShortCutMenu,请见文章最后一节模拟器上测试Shortcut

动态标签的添加

所谓动态标签,就是我们可以通过代码来添加标签,相关的类有:

  • UIApplicationShortcutItem 3DTouch标签的类
  • UIApplicationShortcutIcon 标签中图片Icon的类

响应标签的行为

当点击标签进入应用时,我们需要在代码对不同标签的做处理。在iOS 9 中,UIApplicationDelegate 新增了方法:

- (void) application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler

当通过标签进入APP时,appdelegate中会回调到这个函数。当你处理完了标签需要做的工作之后,需要执行 completionHandler

这里有一个地方需要特别注意一下,用户按下标签进入应用要分成两种状况:启动APP,APP从后台回到前台。你需要在 application:didFinishLaunchingWithOptions:application:willFinishLaunchingWithOptions: 中的一个去检查字典 launchOptions 中是否包含了UIApplicationLaunchOptionsShortcutItemKey。如果有这个key,则表示是启动app。你需要在此时设置好APP的view层级,并且为 application:willFinishLaunchingWithOptions: 返回 false。这样就可以阻止 application(_:performActionForShortcutItem:completionHandler:) 被调用,避免标签处理逻辑被处理两次。

以下是 sample code (Objective_c),demo 放在github上面shortcutDemo

另外苹果官方提供了 swift 的sample code

#import "AppDelegate.h"

@interface AppDelegate ()
@property (nonatomic, retain) UIApplicationShortcutItem * launchedShortcutItem;

@end

@implementation AppDelegate

- (BOOL) handledShortCutItem: (UIApplicationShortcutItem *) shortcutItem
{
    BOOL handled = NO;
    if (!shortcutItem.type) return NO;
    if (![shortcutItem.type isKindOfClass:[NSString class]]) return NO;

    UIAlertController * alertController = [UIAlertController alertControllerWithTitle:@"Shortcut Handled" message:shortcutItem.localizedTitle preferredStyle: UIAlertControllerStyleAlert];
    UIAlertAction *alertAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    }];
    [alertController addAction: alertAction];

    [self.window.rootViewController presentViewController: alertController animated: YES completion:nil];

    return handled;
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
//    Dynamic Shortcuts
    UIApplicationShortcutIcon * addIcon = [UIApplicationShortcutIcon iconWithType: UIApplicationShortcutIconTypeAdd];//Icons should be square, single color,

    UIApplicationShortcutItem * addItem = [[UIApplicationShortcutItem alloc]initWithType: @"add" localizedTitle: @"Add" localizedSubtitle: nil icon: addIcon userInfo: nil];

    [UIApplication sharedApplication].shortcutItems = @[addItem];

    BOOL shouldPerformAdditionalDelegateHandling    = YES;
    if (launchOptions[UIApplicationLaunchOptionsShortcutItemKey]) {
        UIApplicationShortcutItem * shortcutItem = launchOptions[UIApplicationLaunchOptionsShortcutItemKey];
        self.launchedShortcutItem   = shortcutItem;
        // This will block "performActionForShortcutItem:completionHandler" from being called.
        shouldPerformAdditionalDelegateHandling     = NO;
    }
    return shouldPerformAdditionalDelegateHandling;
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    if(!self.launchedShortcutItem) {
        return;
    }
    [self handledShortCutItem: self.launchedShortcutItem];

    self.launchedShortcutItem   = nil;
}

- (void) application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler
{
    BOOL result = [self handledShortCutItem: shortcutItem];
    completionHandler(result);
}

@end

模拟器上测试Shortcuts

Xcode7.0 的模拟器是没办法之间触发出 3D Touch 事件的,好在有人开发出了插件SBShortcutMenuSimulator.实用也很简单,只需要follow readme 中的 usage 便可。以下摘录

git clone https://github.com/DeskConnect/SBShortcutMenuSimulator.git
cd SBShortcutMenuSimulator
make

xcrun simctl spawn booted launchctl debug system/com.apple.SpringBoard --environment DYLD_INSERT_LIBRARIES=$PWD/SBShortcutMenuSimulator.dylib
xcrun simctl spawn booted launchctl stop com.apple.SpringBoard

echo 'com.apple.mobilecal' | nc 127.0.0.1 8000

‘com.apple.mobilecal’ 替换成你需要测试的app的bundle就可以了,例如我的是 chengpei.shortcutDemo

echo 'chengpei.shortcutDemo' | nc 127.0.0.1 8000

上面的命令全都在clone的SBShortcutMenuSimulator目录下执行。

结语

以上。