最近发现的问题 “设置和捷径App内未列出自己App的Shortcut” 的解决办法在文章末尾或者我的github博客
网上的shortcuts介绍都没有提及这一点,导致了一个疏忽,甚至以为是自动添加的。
文章转自自己github博客
后续更新可能不会及时同步到简书,欢迎大家来我的github博客查看指教。
同时github屏蔽了baidu爬虫,我的github博客无法通过百度搜索到,很尴尬😅,不想搭CDN不想搭Coding,暂时没有更好解决办法。
背景介绍
WWDC 2018 苹果更新了Siri使其支持Shortcuts功能,中文名“捷径”。支持用户通过自定义把一系列操作合并到一个Shortcut内。苹果官方应用提供了一些接口供Shortcut调用,如Map应用的“获取行程时间”,“显示路线”等。如果我们的应用需要支持,需要自己在App中开放可以被Shortcuts访问的接口。同时苹果正在大力推广这个Siri新功能,支持软件可在苹果App Store榜单“用 Siri,走捷径”中列出,有一定推广作用。该功能也有利于增加用户粘度和活跃人数。
Apple提供了官方关于Shortcuts的demo,但是在苹果大力推广Swift的浪潮下,demo的语言也是Swift版的,无奈公司现在还在使用Objc,下面介绍下Objc下的接入过程。
创建Custom Intent
在项目中通过“New File...”创建一个Intents.intentdefinition
文件。这个文件用来定义自定义intent
类型。
同时会自动在项目的Info.plist
文件中添加NSUserActivityTypes
添加Frameworks
在项目的Build Phases中的Link Binary With Libraries中添加Intents.framework
和IntentsUI.framework
。
添加Shortcut按钮
在项目中需要调用添加Shortcut按钮的.m文件中,
增加import
#import <Intents/Intents.h>
#import <IntentsUI/IntentsUI.h>
#import "HWTakePhotoIntent.h" //上方“设置Custom Intents”图中右边箭头指的“Class Name”
添加Delegate
<INUIAddVoiceShortcutButtonDelegate,
INUIAddVoiceShortcutViewControllerDelegate,
INUIEditVoiceShortcutViewControllerDelegate>
添加Property
@property (nonatomic, strong) HWTakePhotoIntent API_AVAILABLE(ios(12.0)) *intent;
@property (nonatomic, strong) INUIAddVoiceShortcutButton API_AVAILABLE(ios(12.0)) *shortcutButton;
添加shortcutButton按钮
if (@available(iOS 12.0, *)) {
_shortcutButton = [[INUIAddVoiceShortcutButton alloc] initWithStyle:INUIAddVoiceShortcutButtonStyleWhiteOutline];
_shortcutButton.shortcut = [[INShortcut alloc] initWithIntent:self.intent];
_shortcutButton.translatesAutoresizingMaskIntoConstraints = false;
_shortcutButton.delegate = self;
[self.view addSubview:_shortcutButton];
}
设置intent属性
- (HWTakePhotoIntent *)intent API_AVAILABLE(ios(12.0)){
if (!_intent) {
_intent = [[HWTakePhotoIntent alloc] init];
_intent.suggestedInvocationPhrase = @"开始改作业"; //在Siri语音设置时显示的建议设置唤起文字
}
return _intent;
}
设置对应delegate方法
#pragma mark - INUIAddVoiceShortcutButtonDelegate
- (void)presentAddVoiceShortcutViewController:(INUIAddVoiceShortcutViewController *)addVoiceShortcutViewController forAddVoiceShortcutButton:(INUIAddVoiceShortcutButton *)addVoiceShortcutButton API_AVAILABLE(ios(12.0)){
addVoiceShortcutViewController.delegate = self;
[self presentViewController:addVoiceShortcutViewController animated:YES completion:nil];
}
- (void)presentEditVoiceShortcutViewController:(INUIEditVoiceShortcutViewController *)editVoiceShortcutViewController forAddVoiceShortcutButton:(INUIAddVoiceShortcutButton *)addVoiceShortcutButton API_AVAILABLE(ios(12.0)){
editVoiceShortcutViewController.delegate = self;
[self presentViewController:editVoiceShortcutViewController animated:YES completion:nil];
}
#pragma mark - INUIAddVoiceShortcutViewControllerDelegate
- (void)addVoiceShortcutViewController:(INUIAddVoiceShortcutViewController *)controller didFinishWithVoiceShortcut:(INVoiceShortcut *)voiceShortcut error:(NSError *)error
API_AVAILABLE(ios(12.0)){
[controller dismissViewControllerAnimated:YES completion:nil];
}
- (void)addVoiceShortcutViewControllerDidCancel:(INUIAddVoiceShortcutViewController *)controller API_AVAILABLE(ios(12.0)){
[controller dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - INUIEditVoiceShortcutViewControllerDelegate
- (void)editVoiceShortcutViewControllerDidCancel:(INUIEditVoiceShortcutViewController *)controller API_AVAILABLE(ios(12.0)){
[controller dismissViewControllerAnimated:YES completion:nil];
}
- (void)editVoiceShortcutViewController:(INUIEditVoiceShortcutViewController *)controller didUpdateVoiceShortcut:(INVoiceShortcut *)voiceShortcut error:(NSError *)error API_AVAILABLE(ios(12.0)){
[controller dismissViewControllerAnimated:YES completion:nil];
}
- (void)editVoiceShortcutViewController:(INUIEditVoiceShortcutViewController *)controller didDeleteVoiceShortcutWithIdentifier:(NSUUID *)deletedVoiceShortcutIdentifier API_AVAILABLE(ios(12.0)){
[controller dismissViewControllerAnimated:YES completion:nil];
}
AppDelegate.m文件中添加方法
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
// activityType 和 Info.plist中的NSUserActivityTypes内容对应
if ([userActivity.activityType isEqualToString:@"HWTakePhotoIntent"]) {
[HWNotificationCenter postNotificationName:kNotificationOpenCamera object:nil]; // 通过通知找到对应class处理activity
}
return YES;
}
用于在Siri中通过设定语音调起应用时处理Siri的请求。我们的项目中是打开摄像头拍照批改作业。
iOS12 Siri Known Issues
如上图红框所示,iOS12发布后已知问题之一是系统的INUIAddVoiceShortcutButton
只支持默认的标题“Add to Siri”和“Added to Siri”,没有做本地化支持😂。此外按钮的样式也很死,无法自由自在的自定义,所以灰溜溜的只能自己来写自定义按钮了。
需求一:判断是否添加过该shortcut来区别跳转INUIAddVoiceShortcutViewController
还是INUIEditVoiceShortcutViewController
当然苹果为我们提供了INVoiceShortcutCenter
的- (void)getAllVoiceShortcutsWithCompletion:(void(^)(NSArray<INVoiceShortcut *> * _Nullable voiceShortcuts, NSError * _Nullable error))completionHandler;
方法来获得所有添加的Shortcuts或者- (void)getVoiceShortcutWithIdentifier:(NSUUID *)identifier completion:(void(^)(INVoiceShortcut * _Nullable voiceShortcut, NSError * _Nullable error))completionHandler NS_SWIFT_NAME(getVoiceShortcut(with:completion:));
通过identifier查找对应的Shortcut。
我在项目中的按钮点击事件代码如下:
- (void)shortcutButtonClicked:(UIButton *)sender
{
if (@available(iOS 12.0, *)) {
[[INVoiceShortcutCenter sharedCenter] getAllVoiceShortcutsWithCompletion:^(NSArray<INVoiceShortcut *> * _Nullable voiceShortcuts, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
BOOL tempAddedShortcut = NO;
for (INVoiceShortcut *voiceShortcut in voiceShortcuts) {
if ([voiceShortcut.shortcut.intent isKindOfClass:[HWTakePhotoIntent class]]) {
tempAddedShortcut = YES;
break;
}
}
self.addedShortcut = tempAddedShortcut;
if (self.addedShortcut) {
INUIEditVoiceShortcutViewController *editVoiceShortcutViewController = [[INUIEditVoiceShortcutViewController alloc] initWithVoiceShortcut:voiceShortcuts[0]];
editVoiceShortcutViewController.delegate = self;
[self presentViewController:editVoiceShortcutViewController animated:YES completion:nil];
} else {
INShortcut *shortcut = [[INShortcut alloc] initWithIntent:self.intent];
INUIAddVoiceShortcutViewController *addVoiceShortcutViewController = [[INUIAddVoiceShortcutViewController alloc] initWithShortcut:shortcut];
addVoiceShortcutViewController.delegate = self;
[self presentViewController:addVoiceShortcutViewController animated:YES completion:nil];
}
});
}];
}
}
需求二:通过INUIAddVoiceShortcutViewController
和INUIEditVoiceShortcutViewController
的Delegates更新自定义Shortcut按钮状态
同样也需要用到需求一中提到的判断是否添加过当前Shortcut来更新对应的自定义Shortcut按钮样式。
“设置和捷径App内未列出自己App的Shortcut” 的解决办法
问题描述
按照上文介绍,一步步在自己的App中添加Shortcut功能成功后,我们并不能在系统的设置
应用的Siri 与 搜索
中看到我们刚刚添加的Shortcut,或者在捷径
应用中找到。如下图:
如果我们通过上文中定义的App内的Add to Siri
按钮添加用户的Shortcut到Siri成功后,我们依然可以在设置
和捷径
应用中看到。但是如果删除添加的Shortcut,有都会消失。这显然不是我们需要的效果。
预期效果是不管用户是否在App内添加Shortcut到Siri,都应在设置
和捷径
应用中看到。
解决办法
我们需要自己手动更新我们应用的Shortcut Suggestions的内容,这样才能在设置
和捷径
应用中列出。
更新Shortcut Suggestions内容的代码如下。
- (void)addMenuItemShortcuts
{
if (AVAILABLE(12.0)) {
HWTakePhotoIntent *intent = [[HWTakePhotoIntent alloc] init];
intent.suggestedInvocationPhrase = NSLocalizedString(@"SIRI_SHORTCUT_CORRECT_WORK", nil);
[[INVoiceShortcutCenter sharedCenter] setShortcutSuggestions:@[[[INShortcut alloc] initWithIntent:intent]]];
}
}
我是在AppDelegate
的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
里做了初始化。
更新Shortcut Suggestions内容后,即使不在应用内添加Shortcut到Siri,在设置
和捷径
应用中仍能找到应用提供的Shortcut建议。
设置
中的界面如下
点击捷径
进入后如下
如果你需要,也可以在其他需要的时候更新Shortcut Suggestions内容。
总结
Siri Shortcut大功告成