iOS开发基础152 - 添加快捷方式到主屏幕
iOS 添加快捷方式到主屏幕
iOS 16 引入的 App Intents 框架让 App 的核心功能可以被系统深度整合——用户无需打开 App,就能通过 Siri、Spotlight、快捷指令 App,甚至直接在主屏幕添加快捷方式图标来一键执行。本文从"添加快捷方式到主屏幕"的用户流程讲起,深入解析 App Intents 的实现原理、完整代码(Swift + OC 方案)、底层执行机制,以及 iOS 18 的控制中心新特性。
一、什么是"添加快捷方式到主屏幕"
用户把 App 的某个功能(如"打开收藏"、"开始导航")做成一个像 App 图标一样的快捷方式放在主屏幕,点击后直接执行对应功能,无需打开 App 再层层点击。
用户操作流程
1. 打开「快捷指令」App
↓
2. 找到 App 提供的快捷指令(或自己创建)
↓
3. 点击快捷指令右上角「...」→ 「添加到主屏幕」
↓
4. 自定义图标和名称 → 点击「添加」
↓
5. 自动跳转到 Safari → 再次确认「添加到主屏幕」
↓
6. 主屏幕出现快捷方式图标 ✅
点击图标后发生什么
用户点击主屏幕快捷方式图标
↓
系统通过 URL Scheme 打开「快捷指令」App
↓
快捷指令 App 执行对应的快捷指令
↓
如果包含 App Intent → 系统调用你的 App 处理
↓
App 执行功能(打开页面/执行操作)
↓
返回结果给用户
二、三种实现方案对比
iOS 上实现"快捷方式到主屏幕"有三种技术方案,适用场景不同:
| 方案 | 最低系统 | 语言 | 说明 |
|---|---|---|---|
| App Intents | iOS 16 | Swift-only | 最新推荐,系统自动生成快捷指令,支持 Siri/Spotlight/主屏幕/控制中心 |
| NSUserActivity + Siri Shortcuts | iOS 12 | OC + Swift | 旧方案,通过捐赠用户活动让 Siri 建议快捷指令 |
| Web Clip / PWA | iOS 8+ | 前端技术 | Safari"添加到主屏幕",本质是网页快捷方式,不是原生 App 功能 |
重要说明:App Intents 是 Swift-only 框架(iOS 16 引入时就只支持 Swift),OC 项目无法直接使用。如果你的项目是 OC,可以用 NSUserActivity 方案实现类似的 Siri Shortcuts 功能;或者用 Swift 编写 Intent 代码,通过桥接在 OC 项目中调用。本文会分别提供两种方案的完整代码。
三、方案一:App Intents(iOS 16+,推荐)
用 Swift 定义一个实现 AppIntent 协议的结构体,告诉系统"这个功能叫什么、做什么",再通过 AppShortcutsProvider 注册快捷指令短语,系统就会自动在快捷指令 App、Spotlight、Siri 中显示这个功能,用户可以添加到主屏幕。
3.1 定义 AppIntent
import AppIntents
import UIKit
// 定义一个"打开收藏页"的 Intent
struct OpenFavoritesIntent: AppIntent {
// 快捷指令的标题(显示在快捷指令 App 和主屏幕图标下方)
static var title: LocalizedStringResource = "打开收藏"
// 描述(可选,帮助用户理解这个快捷指令做什么)
static var description = IntentDescription(
"直接打开 App 的收藏页面,无需先启动 App。",
categoryName: "导航"
)
// 可选:打开 App 时执行(需要在 Info.plist 中配置)
static var openAppWhenRun: Bool = true
// 执行逻辑
func perform() async throws -> some IntentResult {
// 在这里执行你的功能
// 例如:发送通知让 App 跳转到收藏页
NotificationCenter.default.post(name: .init("OpenFavorites"), object: nil)
// 返回结果(可以附带值)
return .result()
}
}
3.2 带参数的 AppIntent
// 定义一个"搜索文章"的 Intent,带搜索关键词参数
struct SearchArticlesIntent: AppIntent {
static var title: LocalizedStringResource = "搜索文章"
static var description = IntentDescription("在 App 中搜索指定关键词的文章。")
// 参数:搜索关键词
@Parameter(title: "关键词", description: "要搜索的内容")
var keyword: String
// 参数提供:如果需要从 App 数据中动态提供选项
static var parameterSummary: some ParameterSummary {
Summary("搜索 \(\.$keyword) 的文章")
}
func perform() async throws -> some IntentResult {
// 执行搜索
print("搜索关键词:\(keyword)")
return .result()
}
}
3.3 定义 AppShortcutsProvider
import AppIntents
// 注册 App 提供的所有快捷指令
// 注意:每个 App 只能有一个 AppShortcutsProvider
struct MyAppShortcuts: AppShortcutsProvider {
// 可选:快捷指令磁贴的主题色
static var shortcutTileColor: ShortcutTileColor = .blue
// 必须:定义所有快捷指令
@AppShortcutsBuilder
static var appShortcuts: [AppShortcut] {
// 快捷指令 1:打开收藏
AppShortcut(
intent: OpenFavoritesIntent(),
// Siri 唤醒短语(必须包含 .applicationName,即你的 App 名称)
phrases: [
"打开收藏 in \(.applicationName)",
"用 \(.applicationName) 看收藏"
],
// 短标题(显示在有限空间内)
shortTitle: "收藏",
// 系统图标(SF Symbols)
systemImageName: "star.fill"
)
// 快捷指令 2:搜索文章(带参数)
AppShortcut(
intent: SearchArticlesIntent(),
phrases: [
"在 \(.applicationName) 搜索 \(\.$keyword)",
"用 \(.applicationName) 搜 \(\.$keyword)"
],
shortTitle: "搜索",
systemImageName: "magnifyingglass"
)
}
}
3.4 处理 Intent 打开 App
如果 openAppWhenRun = true,点击快捷方式后会打开 App。在 App 中接收并处理:
// AppDelegate 或 SceneDelegate
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// 监听来自 Intent 的通知
NotificationCenter.default.addObserver(
forName: .init("OpenFavorites"),
object: nil,
queue: .main
) { _ in
// 跳转到收藏页
print("跳转到收藏页")
}
}
3.5 完整的 App 内跳转处理
更好的方式是在 perform() 中直接处理,而不是通过通知:
struct OpenFavoritesIntent: AppIntent {
static var title: LocalizedStringResource = "打开收藏"
static var openAppWhenRun: Bool = true
@MainActor
func perform() async throws -> some IntentResult {
// 获取当前的 SceneDelegate,直接跳转
if let sceneDelegate = UIApplication.shared.connectedScenes
.first?.delegate as? SceneDelegate {
sceneDelegate.navigateToFavorites()
}
return .result()
}
}
// SceneDelegate 中
extension SceneDelegate {
func navigateToFavorites() {
// 执行跳转逻辑
guard let nav = window?.rootViewController as? UINavigationController else { return }
let favoritesVC = FavoritesViewController()
nav.pushViewController(favoritesVC, animated: true)
}
}
四、方案二:NSUserActivity + Siri Shortcuts(iOS 12+,OC 可用)
一句话原理
在用户使用某个功能时,创建一个 NSUserActivity 对象并"捐赠"给系统,系统学习用户习惯后会在 Siri 建议和锁屏中推荐这个快捷操作,用户可以把它添加到主屏幕。
4.1 Info.plist 配置
首先在 Info.plist 中注册 Activity 类型:
<key>NSUserActivityTypes</key>
<array>
<string>com.myapp.openFavorites</string>
<string>com.myapp.searchArticles</string>
</array>
4.2 OC 完整实现
// 定义 Activity 类型常量
NSString *const kActivityTypeOpenFavorites = @"com.myapp.openFavorites";
NSString *const kActivityTypeSearchArticles = @"com.myapp.searchArticles";
@interface ViewController ()
@property (nonatomic, strong) NSUserActivity *favoritesActivity;
@end
@implementation ViewController
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
// 用户进入收藏页时,捐赠 Activity
[self donateFavoritesActivity];
}
// 捐赠"打开收藏"Activity
- (void)donateFavoritesActivity {
// 1. 创建 NSUserActivity
self.favoritesActivity = [[NSUserActivity alloc] initWithActivityType:kActivityTypeOpenFavorites];
// 2. 设置标题(显示在 Siri 建议中)
self.favoritesActivity.title = @"打开收藏";
// 3. 设置用户信息(恢复时用)
self.favoritesActivity.userInfo = @{@"page": @"favorites"};
// 4. 关键:允许被 Siri 预测为快捷指令
self.favoritesActivity.isEligibleForPrediction = YES;
self.favoritesActivity.isEligibleForSearch = YES;
self.favoritesActivity.isEligibleForPublicIndexing = NO;
// 5. 成为当前 Activity(系统会自动捐赠)
[self.favoritesActivity becomeCurrent];
}
// 搜索时捐赠 Activity
- (void)donateSearchActivityWithKeyword:(NSString *)keyword {
NSUserActivity *activity = [[NSUserActivity alloc] initWithActivityType:kActivityTypeSearchArticles];
activity.title = [NSString stringWithFormat:@"搜索 %@", keyword];
activity.userInfo = @{@"keyword": keyword};
activity.isEligibleForPrediction = YES;
activity.isEligibleForSearch = YES;
[activity becomeCurrent];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
// 离开页面时,使 Activity 失效
[self.favoritesActivity resignCurrent];
self.favoritesActivity = nil;
}
@end
4.3 处理快捷指令启动 App
当用户通过主屏幕快捷方式或 Siri 打开 App 时,系统会通过 NSUserActivity 回调:
// AppDelegate 中
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
if ([userActivity.activityType isEqualToString:kActivityTypeOpenFavorites]) {
// 跳转到收藏页
NSLog(@"从快捷指令打开收藏页");
[self navigateToFavorites];
return YES;
}
if ([userActivity.activityType isEqualToString:kActivityTypeSearchArticles]) {
NSString *keyword = userActivity.userInfo[@"keyword"];
NSLog(@"从快捷指令搜索:%@", keyword);
[self navigateToSearchWithKeyword:keyword];
return YES;
}
return NO;
}
- (void)navigateToFavorites {
// 执行跳转逻辑
}
- (void)navigateToSearchWithKeyword:(NSString *)keyword {
// 执行搜索跳转
}
4.4 SceneDelegate 中的处理(iOS 13+)
- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity {
if ([userActivity.activityType isEqualToString:kActivityTypeOpenFavorites]) {
// 跳转到收藏页
}
}
五、方案三:Web Clip / PWA(网页添加到主屏幕)
一句话原理
网页通过 HTML meta 标签声明图标、名称和启动方式,用户在 Safari 中选择"添加到主屏幕",系统就会在主屏幕创建一个网页快捷方式,点击后全屏打开网页(类似原生 App)。
5.1 网页配置
<!DOCTYPE html>
<html>
<head>
<!-- 全屏模式(隐藏 Safari 工具栏,像 App 一样) -->
<meta name="apple-mobile-web-app-capable" content="yes">
<!-- 状态栏样式 -->
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<!-- 主屏幕图标名称 -->
<meta name="apple-mobile-web-app-title" content="我的应用">
<!-- 主屏幕图标(不同尺寸) -->
<link rel="apple-touch-icon" href="icon-180.png" sizes="180x180">
<link rel="apple-touch-icon" href="icon-152.png" sizes="152x152">
<!-- 启动画面 -->
<link rel="apple-touch-startup-image" href="splash.png">
<title>我的应用</title>
</head>
<body>
<h1>Hello PWA</h1>
</body>
</html>
5.2 与原生 App 快捷方式的区别
| 对比 | App Intents 快捷方式 | Web Clip |
|---|---|---|
| 本质 | 原生 App 功能快捷方式 | 网页快捷方式 |
| 点击后 | 打开原生 App 执行功能 | 全屏打开网页 |
| 离线可用 | 取决于 App | 取决于 PWA 缓存 |
| 审核 | App Store 审核 | 无需审核 |
| 功能限制 | 原生功能全支持 | 受浏览器限制 |
六、底层原理:快捷指令如何添加到主屏幕
一句话原理
快捷指令 App 的"添加到主屏幕"功能,本质是生成一个特殊的 Web Clip(网页快捷方式),通过 Safari 的"添加到主屏幕"机制创建图标,图标点击后通过 URL Scheme 打开快捷指令 App 并执行对应指令。
详细流程
用户点击「添加到主屏幕」
↓
快捷指令 App 生成一个 data: URL
(包含 base64 编码的 HTML,HTML 中包含重定向到快捷指令的 URL Scheme)
↓
用 Safari 打开这个 data: URL
↓
Safari 显示一个提示页,引导用户点击分享 → 添加到主屏幕
↓
系统创建 Web Clip 图标(包含自定义图标和名称)
↓
用户点击主屏幕图标
↓
Web Clip 打开内置的 HTML 页面
↓
HTML 页面通过 URL Scheme 跳转到快捷指令 App
(如:shortcuts://run-shortcut?name=xxx)
↓
快捷指令 App 执行对应指令
↓
如果指令包含 App Intent → 调用你的 App
↓
App 执行功能 ✅
为什么需要 Safari 中转
iOS 没有提供"直接在主屏幕创建图标"的公开 API(第三方 App 无法直接创建主屏幕图标)。唯一的公开方式是通过 Safari 的"添加到主屏幕"功能(Web Clip)。所以快捷指令 App 必须:
- 生成一个网页(data: URL)
- 用 Safari 打开
- 让用户手动点击"添加到主屏幕"
这就是为什么添加快捷方式到主屏幕时会跳转到 Safari 的原因。
七、iOS 18 新特性:控制中心(Control Center)
一句话原理
iOS 18 重新设计了控制中心,第三方 App 可以通过 App Intents + ControlWidget 把自己的功能添加到控制中心,用户下拉控制中心就能一键执行,比主屏幕快捷方式更快捷。
7.1 实现 ControlWidget
import AppIntents
import SwiftUI
import WidgetKit
// 定义一个控制中心控件
struct MyAppControl: ControlWidget {
// 控件的主体
var body: some ControlWidgetConfiguration {
StaticControlConfiguration(
kind: "com.myapp.favoritesControl"
) {
// 控件显示的内容
ControlWidgetButton(action: OpenFavoritesIntent()) {
Label("收藏", systemImage: "star.fill")
}
}
.displayName("打开收藏")
.description("一键打开收藏页")
}
}
7.2 用户添加到控制中心
1. 下拉打开控制中心
↓
2. 长按空白区域进入编辑模式
↓
3. 点击「+」打开控件库
↓
4. 找到你的 App,选择控件
↓
5. 添加到控制中心 ✅
7.3 控制中心 vs 主屏幕快捷方式
| 对比 | 主屏幕快捷方式 | 控制中心控件 |
|---|---|---|
| 位置 | 主屏幕图标 | 下拉控制中心 |
| 访问速度 | 需要回到主屏幕 | 任意界面下拉即可 |
| 技术 | App Intents + Web Clip | App Intents + ControlWidget |
| 最低系统 | iOS 16 | iOS 18 |
| 自定义外观 | 自定义图标 | 系统统一风格 |
八、Swift 版本对照
App Intents 完整示例(Swift)
import AppIntents
import UIKit
// MARK: - 1. 定义 Intent
struct OpenFavoritesIntent: AppIntent {
static var title: LocalizedStringResource = "打开收藏"
static var description = IntentDescription("直接打开收藏页面")
static var openAppWhenRun: Bool = true
@MainActor
func perform() async throws -> some IntentResult {
if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
let nav = scene.windows.first?.rootViewController as? UINavigationController {
let favoritesVC = FavoritesViewController()
nav.pushViewController(favoritesVC, animated: true)
}
return .result()
}
}
// MARK: - 2. 带参数的 Intent
struct SearchArticlesIntent: AppIntent {
static var title: LocalizedStringResource = "搜索文章"
@Parameter(title: "关键词")
var keyword: String
func perform() async throws -> some IntentResult {
print("搜索:\(keyword)")
return .result()
}
}
// MARK: - 3. 注册快捷指令
struct MyAppShortcuts: AppShortcutsProvider {
static var shortcutTileColor: ShortcutTileColor = .blue
@AppShortcutsBuilder
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: OpenFavoritesIntent(),
phrases: ["打开收藏 in \(.applicationName)"],
shortTitle: "收藏",
systemImageName: "star.fill"
)
AppShortcut(
intent: SearchArticlesIntent(),
phrases: ["在 \(.applicationName) 搜索 \(\.$keyword)"],
shortTitle: "搜索",
systemImageName: "magnifyingglass"
)
}
}
NSUserActivity 完整示例(Swift)
import UIKit
class FavoritesViewController: UIViewController {
private var userActivity: NSUserActivity?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
donateActivity()
}
private func donateActivity() {
let activity = NSUserActivity(activityType: "com.myapp.openFavorites")
activity.title = "打开收藏"
activity.userInfo = ["page": "favorites"]
activity.isEligibleForPrediction = true
activity.isEligibleForSearch = true
activity.becomeCurrent()
self.userActivity = activity
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
userActivity?.resignCurrent()
userActivity = nil
}
}
// AppDelegate 中处理
func application(_ application: UIApplication, continue userActivity: NSUserActivity,
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
if userActivity.activityType == "com.myapp.openFavorites" {
// 跳转到收藏页
return true
}
return false
}
九、常见问题与坑
Q1:App Intents 可以用 Objective-C 写吗?
不能。App Intents 是 Swift-only 框架(iOS 16 引入时就只支持 Swift)。OC 项目有两个选择:
- 用 NSUserActivity 方案(iOS 12+,OC 完全支持)。
- 用 Swift 编写 Intent 代码,通过桥接在 OC 项目中调用(需要混编配置)。
Q2:为什么我的 App Shortcuts 没有出现在快捷指令 App 中?
检查以下几点:
- 是否实现了
AppShortcutsProvider协议。 appShortcuts是否用@AppShortcutsBuilder标注。- phrases 中是否包含
\(.applicationName)(必须包含 App 名称)。 - 重新编译运行 App(系统需要扫描 App 中的 Intent 定义)。
- 打开快捷指令 App,在"App"分类中找到你的 App。
Q3:添加到主屏幕后,点击图标没反应?
可能的原因:
- 快捷指令本身有问题,先在快捷指令 App 中测试能否正常运行。
openAppWhenRun没有设置为 true,导致 App 没有被打开。perform()方法中有错误,检查是否抛出异常。- 确保快捷指令 App 没有被卸载(主屏幕快捷方式依赖快捷指令 App)。
Q4:主屏幕快捷方式和快捷指令 App 是什么关系?
主屏幕上的快捷方式图标不是独立的,它依赖快捷指令 App:
- 点击图标 → 打开快捷指令 App → 执行指令。
- 如果卸载了快捷指令 App,主屏幕快捷方式就无法使用。
- 快捷指令 App 是系统预装的,用户无法卸载(iOS 13+)。
Q5:可以直接在 App 内调用"添加到主屏幕"吗?
不能。iOS 没有提供公开 API 让第三方 App 直接在主屏幕创建图标。唯一的方式是引导用户:
- 打开快捷指令 App
- 找到快捷指令
- 手动点击"添加到主屏幕"
你可以在 App 内添加引导提示,告诉用户如何操作,但无法自动完成。
Q6:App Shortcuts 最多可以定义多少个?
每个 App 最多 10 个 App Shortcut(通过 AppShortcutsProvider 注册)。超过的部分不会被系统识别。建议只提供最核心、最高频的功能。
Q7:Siri 唤醒短语有什么要求?
- 必须包含
\(.applicationName)(即你的 App 名称)。 - 短语要自然,符合口语习惯。
- 每个 App Shortcut 可以提供多个短语。
- 示例:"打开收藏 in 我的App"、"用我的App看收藏"。
Q8:如何让快捷指令在 Spotlight 中显示?
实现了 AppShortcutsProvider 后,系统会自动在 Spotlight 中索引你的快捷指令。用户在 Spotlight 搜索关键词时,相关的快捷指令会出现在搜索结果中,点击即可执行。
Q9:NSUserActivity 捐赠后多久会出现在 Siri 建议中?
不确定。系统会根据用户的使用习惯、时间、地点等因素智能推荐。通常需要用户多次使用该功能后,系统才会开始推荐。可以在 设置 → Siri 与搜索 → 你的 App 中手动管理建议。
Q10:iOS 18 控制中心控件和主屏幕快捷方式可以同时用吗?
可以。两者是独立的:
- 主屏幕快捷方式:通过 App Intents + 快捷指令 App 实现。
- 控制中心控件:通过 App Intents + ControlWidget 实现。
- 可以用同一个 AppIntent 同时支持两种入口。
十、总结
- 三种方案:
- App Intents(iOS 16+,Swift-only):最新推荐,系统自动生成快捷指令,支持 Siri/Spotlight/主屏幕/控制中心。
- NSUserActivity(iOS 12+,OC/Swift):旧方案,通过捐赠用户活动让 Siri 建议快捷指令。
- Web Clip/PWA:网页添加到主屏幕,本质是网页快捷方式。
- App Intents 核心步骤:
- 定义
AppIntent结构体(标题、描述、perform 执行逻辑)。 - 定义
AppShortcutsProvider(注册快捷指令短语、图标)。 - 在
perform()中处理跳转或执行功能。 - 用户在快捷指令 App 中添加到主屏幕。
- 定义
- 底层原理:快捷指令 App 通过生成 Web Clip(data: URL + Safari)实现主屏幕图标创建,点击后通过 URL Scheme 打开快捷指令 App 执行指令。
- iOS 18 新特性:控制中心支持第三方 App 控件(ControlWidget),比主屏幕快捷方式更快捷。
- 关键限制:App Intents 是 Swift-only;每个 App 最多 10 个快捷指令;无法直接在 App 内创建主屏幕图标;主屏幕快捷方式依赖快捷指令 App。

浙公网安备 33010602011771号