适用于 iOS 的主屏幕快速操作

2,897 阅读14分钟

快速操作是让您的用户在主屏幕中快速访问您应用的常用功能的好方法。iOS 13 引入了快速操作的概念,用户可以在其中触摸并按住应用程序图标以显示一组快捷方式或操作,以便直接从主屏幕执行。

默认情况下,所有应用程序都具有编辑主屏幕或删除应用程序的快捷操作:


长按主屏幕上的“Note Buddy”应用程序图标后出现的菜单,其中包含“编辑”主屏幕或“删除”应用程序的选项。

开发者还可以提供自己的快捷操作,为用户提供常用应用功能的强大快捷方式。iOS 相机应用程序具有拍摄不同类型的照片或录制视频的操作。购物应用程序可能会让您直接跳转到您的订单或愿望清单,而消息传递应用程序可能会显示您最喜欢的联系人,以便您轻松访问它们。

我敢肯定,您可以想出快速行动可以使您的用户受益的方式。在本教程中,您将了解:

  • 静态快速​​操作,始终可用于您的应用。
  • 动态快速操作,您的应用可以在运行时定义。
  • 如何在示例项目中支持这两种类型的快速操作,一个名为Note Buddy的笔记应用程序。

入门

Note Buddy 是一款具有一些基本功能的笔记应用程序:

  • 添加、编辑或删除带有标题和正文的注释。
  • 收藏一个笔记。
  • 在启动之间存储您的笔记。
  • 按上次修改日期自动排序笔记。

打开项目,然后构建并运行。您将看到此注释页面:


首次启动后启动项目“Note Buddy”的屏幕截图显示了默认笔记列表和创建新笔记的选项。

为了增强 Note Buddy 的功能,您可以在Models组中找到Note模型及其关联NoteStore的类。**

转到 SwiftUI,在Views组中,您有:

  • NoteList:显示按上次修改日期排序的所有笔记列表。
  • NoteRow:笔记列表中笔记行的视图。它显示笔记的标题、正文以及用户是否将其标记为收藏。
  • EditNote:用于修改笔记标题、正文或更改收藏状态的编辑器。
  • NavigationLink+Value:一个辅助扩展,当您有一个关联的数据模型要推送时,使编程导航更容易一些。

最后,AppMain通过引用NoteStore和主体来描述您的应用程序,该主体在 a 中设置NoteList视图WindowGroup并正确注入适当的环境值。

在开始编码之前,是时候仔细看看这两种类型的快速操作是什么,以及它们是如何工作的。

静态与动态快速操作

您可以使用两种类型的快速操作:静态和动态。

您将静态操作用于在您的应用程序中永远不会更改的操作,例如邮件应用程序的新消息操作。

如果您的操作可能在某些条件下发生变化或取决于特定数据或状态,请使用动态操作。例如,消息应用程序将为您所有固定的对话添加快速操作。

在这两种情况下,您都添加代码来处理触发的特定操作。由于添加静态快速操作更快,您将从这些开始。

创建静态快速操作

静态快速​​操作是让您的用户创建新笔记的好方法。

一张带有笑脸的纸表情。

首先,添加模型代码以帮助您处理触发的操作。右键单击Models组,然后单击New File...  ▸ Swift File。将新文件命名为Action并单击Create

将文件内容替换为:

导入 UIKit

// 1
枚举动作类型:字符串 {
  case newNote = "NewNote"
}

// 2
枚举行动:平等{
  案例新注

  // 3
  init?(shortcutItem: UIApplicationShortcutItem) {
    // 4
    守卫让 type = ActionType(rawValue:shortcutItem.type) else {
      返回零
    }

    // 5
    开关类型{
    案例.new注意:
      自我 = .newNote
    }
  }
}

// 6ActionService: ObservableObject {
  静态让共享 = ActionService()

  // 7
  @Published var 动作:动作?
}

很多事情正在发生。这是您添加的内容:

  1. 您创建一个ActionTypeString. 稍后您将使用字符串值来帮助识别您的应用将执行的不同类型的操作。该newNote案例将确定创建新注释的操作。
  2. 您创建另一个名为的枚举Action,看起来相似,但只是Equatable. 这可能看起来有点重复,但是当您稍后添加其他操作时它会很有意义。
  3. 然后创建一个可失败的初始化程序,它接受UIApplicationShortcutItem. 系统使用这种类型来描述不同的快速动作。
  4. 在这里,您确保您正在创建Action一个已知的ActionType,否则您返回nil
  5. 打开ActionType您的应用程序已知的不同可能值。然后,您可以使用可用的信息来描述正确的Action.
  6. 定义一个ObservableObject类,您可以稍后将其传递到 SwiftUI 环境中,并为您以后使用 UIKit 代码提供一个单例访问器。
  7. 定义一个@Published可以表示您的应用应执行的操作的属性。

Action概念的目的是让您对应用支持的快速操作进行建模,并在UIApplicationShortcutItem.

现在,打开AppMain.swift。在上面noteStore,添加一个新属性ActionService

私有让 actionService = ActionService.shared

然后更新body实现以确保服务被注入到视图层次结构中并且NoteList可以访问它:

var body: 一些场景 {
  窗口组 {
    笔记列表()
      .environmentObject(actionService)
      .environmentObject(noteStore)
      .environment(.managedObjectContext, noteStore.container.viewContext)
  }
}

ActionService在 SwiftUI 环境中可用,是时候使用它了。在Views组中打开NoteList.swift并在下面添加以下属性:**noteStore

@EnvironmentObject var actionService: ActionService
@Environment(.scenePhase) var 场景相位

您不仅要访问ActionService该类,还需要访问ScenePhase,这是一个属性,当您的应用程序变为活动状态和进入后台时会通知您。

在视图的底部,添加以下方法:

func performActionIfNeeded() {
  // 1
  守卫让行动 = actionService.action else { return }

  // 2
  切换动作{
  案例.new注意:
    创建新笔记()
  }

  // 3
  actionService.action = nil
}

这个方法做了三件事:

  1. 检查ActionService.
  2. 打开动作类型并调用 的createNewNote()动作Action.newNote
  3. 从执行后删除操作ActionService

每当您的应用程序处于活动状态时,您都需要触发此代码。您将onChange(of:perform:)视图修饰符与scenePhase您之前添加的属性一起使用。

toolbar在修饰符的右大括号后添加以下代码:

// 1
.onChange(of: scenePhase) { newValue in
  // 2
  切换新值 {
  案例.活动:
    执行ActionIfNeeded()
  // 3
  默认:
    休息
  }
}

在这里,代码:

  1. 将修饰符添加到将在更改List时触发其关闭的修饰符。scenePhase
  2. 使用提供的参数,它会打开该值。如果是.active,它将调用您的新performActionIfNeeded()方法。
  3. 由于您不关心其他状态,例如.inactiveor .background,它不会做任何事情。

修改信息属性列表文件

您将逻辑添加到处理操作的代码中。但在运行应用程序之前,您仍然需要告诉系统您的静态操作。这很简单。

资源组中,打开Info.plist。右键单击顶部的信息属性列表,然后单击 添加行

Key列中,键入Home Screen Shortcut Items,然后按回车键完成编辑。Xcode 自动将Type设置为Array并为您添加一个项目。通过单击 V 形展开元素,您将看到两个嵌套键:


Xcode 的 Info.plist GUI 显示“主屏幕快捷方式项”属性及其默认嵌套值

此数组中列出的每个项目代表您的应用支持的单个静态快速操作。使用以下值更新占位符项:

  • 快捷方式项目类型NewNote
  • 标题New Note

除了默认键之外,您还需要添加一个。将鼠标悬停在标题上,然后单击出现的 + 图标。键入UIApplicationShortcutItemIconSymbolName(暂时忽略弹出选项,您必须键入完整的字符串)KeyValuesquare.and.pencil**

以下是每个键的细分:

  • Shortcut Item Type:一个字符串标识符,代表一种独特的操作类型。您可能会注意到这与ActionType.newNote您之前添加的枚举案例的原始值相匹配。这很重要。
  • 标题:当用户点击并按住您的应用程序图标时显示的用户友好标题。
  • UIApplicationShortcutItemIconSymbolName:用于此操作的 SF 符号。您还可以使用UIApplicationShortcutItemIconFile
    捆绑包中可用的图标文件,或UIApplicationShortcutItemIconType一组可用于快速操作的预定义选项。编辑 Info.plist 条目时,可以从下拉列表中选择图标文件和图标类型,但符号选项不是。


Xcode 的 Info.plist GUI 显示“主屏幕快捷方式项目”属性以及“新注释”快捷方式的值

您可以在Apple 文档中阅读有关Info.plist中快捷方式项目的附加键。

构建和运行您的应用程序

构建并运行您的应用程序。返回主屏幕并长按 Note Buddy 应用程序图标以查看列表中现在可用的快速操作:


“Note Buddy”的快速操作菜单现在包括“New Note”静态操作

点击新笔记以查看会发生什么:


Note Buddy 中的笔记列表屏幕。

没有什么?!?

嗯,这很奇怪。您添加了一个模型来处理应用程序中的操作,并在Info.plist 中定义了一个静态操作。

那怎么了?

当用户与快速操作进行交互时,系统会告诉您的应用程序,但您尚未对该信息进行任何操作。是时候这样做了!

处理静态快速操作

SwiftUI 尚未提供任何原生机制来响应快速操作的启动事件。因此,这部分需要依赖 UIKit。

右键单击AppMain.swift并选择New File...  ▸ Swift File。将文件命名为AppDelegate并单击Create。将文件内容替换为:

// 1
导入 UIKit

// 2AppDelegate: NSObject, UIApplicationDelegate {
  私有让 actionService = ActionService.shared

  // 3
  功能应用程序(
    _ 应用程序:UIApplication,
    configurationForConnectingconnectingSceneSession: UISceneSession,
    选项:UIScene.ConnectionOptions
  ) -> UISceneConfiguration {
    // 4
    如果让shortcutItem = options.shortcutItem {
      actionService.action = Action(shortcutItem:shortcutItem)
    }

    // 5
    让配置 = UISceneConfiguration(
      名称:connectingSceneSession.configuration.name,
      sessionRole:connectingSceneSession.role
    )
    configuration.delegateClass = SceneDelegate.self
    返回配置
  }
}

// 6SceneDelegate: NSObject, UIWindowSceneDelegate {
  私有让 actionService = ActionService.shared

  // 7
  函数窗口场景(
    _windowScene:UIWindowScene,
    performActionForshortcutItem: UIApplicationShortcutItem,
    completionHandler: @escaping (Bool) -> Void
  ) {
    // 8
    actionService.action = Action(shortcutItem:shortcutItem)
    完成处理程序(真)
  }
}

如果你对 UIKit 不太熟悉,别担心!以下是上述代码的概述:

  1. 导入 UIKit 框架以访问此文件中所需的符号和类型。
  2. 创建一个AppDelegate继承自协议NSObject并符合UIApplicationDelegate协议的命名类。
  3. 实现application(_:configurationForConnecting:options:)挂钩到应用程序准备启动主 UI 时触发的事件。
  4. shortcutItem选项一起打开包装。如果存在,则表明用户正在通过快速操作启动您的应用程序。使用您之前添加的初始化程序将数据映射到 anAction并将其分配给ActionService.
  5. 通过创建适当的UISceneConfiguration对象并返回它来满足方法的要求。
  6. 与第二步类似,创建另一个符合UIWindowSceneDelegate协议的类。
  7. 实现windowScene(_:performActionFor:completionHandler:) 挂钩在您的应用程序启动后用户与快速操作交互时触发的事件,例如,当它在后台时。
  8. 与第四步类似,尝试转换UIApplicationShortcutItem为 anAction并将其传递给ActionService.

最后,您的 SwiftUI 应用需要使用新的AppDelegateSceneDelegate. 因此,在AppMain.swift的 下方actionService,添加以下属性AppMain

@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

是时候再次构建和运行您的应用程序了。返回主屏幕并再次尝试使用您的快速操作:


快速操作启动 Note Buddy 并创建新笔记的动画片段

极好的!您添加了您的第一个静态快速操作。虽然这为许多可能性打开了大门,但您无法使用运行时使用的信息对其进行自定义。这就是动态动作的用武之地。

创建动态快速操作

如果用户想要编辑他们最近的笔记,他们仍然需要打开应用程序,滚动列表以找到它,点击它然后进行编辑。

简化和加快流程会很棒!

Info.plist 中进行静态操作时,您可以在代码中添加所有动态操作。对于 Note Buddy,可能对于您的许多应用程序,这些动态操作将在您的场景阶段更改为background. 因此,在前往主屏幕之前,您可以设置一些操作以在最后修改日期之前编辑最新的笔记。

处理动态快速操作

要添加动态操作,首先向您的ActionTypeAction枚举添加一个新案例。

打开Action.swift并将以下内容添加到ActionType

case editNote = "EditNote"

将以下新案例添加到Action

案例编辑注(标识符:字符串)

这就是为什么ActionType并且Action需要是两个单独的枚举。whileActionType表示不同快速动作类型的标识符,Action表示动作本身。在某些情况下,例如 的情况.editNote,这可以包括其他关联值。

添加新的枚举用例后,您会注意到编译器友好地告诉您,switch 语句init?(shortcutItem:)不再是详尽无遗的。通过将以下情况添加到开关来解决此问题:

案例.edit注意:
  if let identifier = shortcutItem.userInfo?["NoteID"] as? 细绳 {
    self = .editNote(标识符:标识符)
  } 别的 {
    返回零
  }

在这里,您将检查shortcutItem'userInfo字典以获取要编辑的注释的 ID。如果成功,您将.editNote使用关联值初始化操作。否则,初始化程序只是通过返回而失败nil

如果您现在尝试构建项目,您将在NoteList.swift中看到另一个编译失败。转到它,您会发现您还需要在内部实现新的操作处理performActionIfNeeded()

将以下代码添加到 switch 语句中:

case .editNote(let 标识符):
  selectedNote = noteStore.findNote(withIdentifier: 标识符)

构建并运行您的应用程序。然后前往主屏幕,长按 Note Buddy 图标并查看结果:


注意 Buddy 的快速操作菜单,其中只有一个“新笔记”选项。

再一次……什么都没有?

虽然您已经确保快速操作正确传递到您的应用程序,但您仍然没有告诉您的应用程序显示任何动态项目。接下来你会这样做。

添加您的动态快速操作

如前所述,添加动态操作的好地方是应用程序进入后台时。此时,您拥有运行代码所需的所有信息,该代码确定要添加哪些操作,然后添加它们。

由于您已经在观察ScenePhasein NoteList,因此这是更新操作的好地方。但在您这样做之前,您需要创建自己的实例UIApplicationShortcutItem来描述您的动态操作。

前往Note.swift并在文件顶部添加以下导入:

导入 UIKit

Note扩展中,添加以下属性:

// 1
var 快捷方式项:UIApplicationShortcutItem?{
  // 2
  守卫!wrappedTitle.isEmpty || !wrappedBody.isEmpty else { return nil }

  // 3
  返回 UIApplicationShortcutItem(
    类型:ActionType.editNote.rawValue,
    本地化标题:“编辑注释”,
    本地化字幕:wrappedTitle.isEmpty ?包裹体:包裹标题,
    图标:.init(systemImageName:isFavorite?“星”:“铅笔”),
    用户信息: [
      “NoteID”:标识符为 NSString
    ]
  )
}

下面是代码分解:

  1. Note您在called上定义了一个新的计算属性shortcutItem。该属性是可选的,因为您不需要将每个音符都表示为快速操作。

  2. 如果注释没有标题或正文,则返回nil以便不会显示该注释。

  3. 然后你初始化一个新的实例UIApplicationShortcutItem并返回它。

    • 使用 定义的类型,ActionType以便在您稍后读回时与预期值匹配。
    • 提供一个用户友好的标题,就像你在Info.plist 中所做的那样。
    • 要进一步个性化操作,您可以将注释的标题或正文作为快速操作的副标题。
    • 使用适当的 SF 符号来表示该项目是否是收藏夹。
    • 在 中包含唯一标识符,Note以便userInfo您在响应快速操作时知道要编辑哪个注释。

现在您创建了一个属性来为给定的 公开适当的快捷方式Note,导航回NoteList.swift。在下面添加以下方法performActionIfNeeded()

func updateShortcutItems() {
  UIApplication.shared.shortcutItems = notes.compactMap(.shortcutItem)
}

在这里,您压缩映射 notes 数组以生成仅包含非 nil 的数组UIApplicationShortcutItem。然后将此数组分配给UIApplication'sshortcutItems属性,该属性又与您在主屏幕菜单中的静态操作一起可用。

现在,您只需在应用程序进入后台时调用此方法。向上滚动到onChange(of: perform:)您之前添加的修饰符并在 switch 语句中添加以下内容:

案例背景:
  updateShortcutItems()

构建并运行。然后看看有哪些项目可用:


快速操作启动 Note Buddy 并创建新笔记的动画片段

耶!你完成了。

灰色笑脸图释。

对您的笔记进行一些编辑,收藏一些并重复该过程。显示的注释以及订单在您后台运行应用程序时保持完美同步:


演示如何更新快速操作以反映应用内更改的动画剪辑

您可能已经注意到,并非所有笔记都显示出来。iOS 将操作数量限制为仅显示适合屏幕的操作。但是,文档建议您不要专门限制动态操作的计数。相反,像 Note Buddy 那样做。

这样,iOS 将显示它可以适应的所有快速操作。如果未来的更新能够实现更多功能,那就太好了,因为您无需在后台应用程序之前推出应用程序更新来支持添加更多操作。

结论

有了它:您已准备好为您的应用程序和项目添加快速操作。

点击下载项目资料与查看原文

这里也推荐一些面试相关的内容!