这篇文章介绍了如何在SwiftUI应用程序中创建主屏幕快捷操作。它解释了如何创建动态菜单,并在选择快速操作按钮时传递数据。此外,它还提供了关于SwiftUI应用程序生命周期中场景管理的一些信息。
应用使用快捷的主屏幕链接是常用的快速进入到某一个功能所必备的方式。那么如何让应用支持主屏幕快捷操作呢?
本文为翻译内容,来自Jeeva Tamilselvan 
本文有删减,推荐访问英文原文地址:Home Screen Quick Actions — SwiftUI 2.0 
翻译: 张洪Heo(转载注明出处)
非原创内容须知 
 
iOS 12中引入了主屏幕快速操作。这是一个快捷按钮,可以将用户导航到应用程序中的特定位置。如今,许多应用程序都带有这种快速行动功能。在这篇文章中,我们将学习如何在SwiftUI应用生命周期(2020)中实现“主屏快速操作”。
我们开始吧
创建动态菜单 动态快速操作在运行时添加到应用程序。这些快速操作是分配给UIApplication的共享实例的UIApplicationShortcutItem数组。
让我们在App struct(也就是SwiftUI生命周期中应用的入口点struct)中创建一个函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @main struct  Quick_ActionsApp : App      @Environment (\.scenePhase) var  phase          var  body: some  Scene  {         WindowGroup  {             ListView ()         }         .onChange(of: phase) { (newPhase) in              switch  newPhase {             case  .active :                 print ("App in active" )             case  .inactive:                  print ("App is inactive" )             case  .background:                 print ("App in Back ground" )                 addQuickActions()              @unknown  default :                 print ("default" )             }         }     }         func  addQuickActions ()         UIApplication .shared.shortcutItems =  [             UIApplicationShortcutItem (type: "Call" , localizedTitle: "Call" ),             UIApplicationShortcutItem (type: "Chat" , localizedTitle: "Chat" ),             UIApplicationShortcutItem (type: "Status" , localizedTitle: "Status" ),             UIApplicationShortcutItem (type: "Contacts" , localizedTitle: "Contacts" ),         ]     } } 
在这里,我们创建了四个快捷方式-呼叫、聊天、状态、联系人。
当应用程序进入后台状态时,我们需要调用此方法。因此,我们使用@Environment(\.sceneProgress)环境变量和onChange(of:)修饰符来捕获应用程序的状态。
在转换到后台状态期间是更新任何动态快速操作的好时机,因为此代码总是在用户返回到主屏幕之前执行。
 
苹果官方文档 指示我们可以在应用程序进入后台阶段时添加动态快速动作。因此,我们在.back中调用addQuickActions()方法
参考这篇文章 可以更好地理解SwiftUI应用程序生命周期中的场景管理。
实际上,我们可以添加任意数量的快捷操作,但苹果最多只能显示四个快捷键。
到目前为止,我们已经看到了如何添加动态快速操作按钮。下一步是如何在选择快速操作按钮时传递数据?
让我们看看这个。
通过快速操作传递数据 要通过快速操作将数据传递到应用程序,我们需要稍微修改addQuickActions()方法。我们将使用下面的UIApplicationShortcutItem构造函数。
1 UIApplicationShortcutItem (type: String , localizedTitle: String , localizedSubtitle: String ?, icon: UIApplicationShortcutIcon ?, userInfo: [String : NSSecureCoding ]? )
在这里,我们通过userInfo字典将数据传递给应用程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func  addQuickActions ()        var  calluserInfo: [String : NSSecureCoding ] {             return  ["name"  : "call"  as  NSSecureCoding ]         }         var  chatuserInfo: [String : NSSecureCoding ] {             return  ["name"  : "chat"  as  NSSecureCoding ]         }         var  statususerInfo: [String : NSSecureCoding ] {             return  ["name"  : "status"  as  NSSecureCoding ]         }         var  contactuserInfo: [String : NSSecureCoding ] {             return  ["name"  : "contact"  as  NSSecureCoding ]         }                  UIApplication .shared.shortcutItems =  [             UIApplicationShortcutItem (type: "Call" , localizedTitle: "Call" , localizedSubtitle: "" , icon: UIApplicationShortcutIcon (type: .message), userInfo: calluserInfo),             UIApplicationShortcutItem (type: "Chat" , localizedTitle: "Chat" , localizedSubtitle: "" , icon: UIApplicationShortcutIcon (type: .message), userInfo: chatuserInfo),             UIApplicationShortcutItem (type: "Status" , localizedTitle: "Status" , localizedSubtitle: "" , icon: UIApplicationShortcutIcon (type: .captureVideo), userInfo: statususerInfo),             UIApplicationShortcutItem (type: "Contacts" , localizedTitle: "Contacts" , localizedSubtitle: "" , icon: UIApplicationShortcutIcon (type: .contact), userInfo: contactuserInfo),         ]     } 
现在,我已经为每个快速操作创建了四个变量,并将它们传递给UIApplicationShortcutItem。如果你仔细观察,会发现每件物品都添加了图标。
下一步如何根据快速动作执行动作。我们走吧👇
执行快速操作操作 当用户点击快速操作时,我们需要处理两个地方。
如果应用程序完全关闭,则会全新打开
如果应用程序在后台,请点击快速操作。
 
为此,我们需要实现两个方法,
application(_:configurationForConnecting:options:) — AppDelegate application(_:performActionFor:completionHandler:) — SceneDelegate  
我们知道SwiftUI应用程序生命周期没有AppDelegate和SceneDelegate。我们先添加AppDelegate,
在SwiftUI应用生命周期中添加AppDelegate和SceneDelegate 1 2 class  AppDelegate : NSObject , UIApplicationDelegate  } 
创建一个名为AppDelegate的类,然后在App struct的开头添加下面的代码片段。紧随其后的是“@Environment(.scenePhase) var phase”
1 @UIApplicationDelegateAdaptor (AppDelegate .self ) var  appDelegate
现在,我们已将AppDelegate添加到SwiftUI生命周期。我们可以在AppDelegate类中添加任何AppDelegate方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class  AppDelegate : NSObject , UIApplicationDelegate      func  application (_  application : UIApplication , configurationForConnecting  connectingSceneSession : UISceneSession , options : UIScene .ConnectionOptions )UISceneConfiguration  {         if  let  shortcutItem =  options.shortcutItem {             shortcutItemToProcess =  shortcutItem         }                  let  sceneConfiguration =  UISceneConfiguration (name: "Custom Configuration" , sessionRole: connectingSceneSession.role)         sceneConfiguration.delegateClass =  CustomSceneDelegate .self                   return  sceneConfiguration     } } class  CustomSceneDelegate : UIResponder , UIWindowSceneDelegate      func  windowScene (_  windowScene : UIWindowScene , performActionFor  shortcutItem : UIApplicationShortcutItem , completionHandler : @escaping  (Bool ) -> Void )         shortcutItemToProcess =  shortcutItem     } } 
在application(_:configurationForConnecting:options:)方法内部,使用UISceneConfiguration(name: sessionRole:)配置SceneDelegate类(CustomSceneDelegate)
在类外部创建UIApplicationShorcutItem变量。因为,我们需要在App struct、AppDelegate、CustomSceneDelegate类中使用此变量。
使用options.shourtcutItem在application(_:configurationForConnecting:options:)方法中和shourtcutItem在application(_:performActionFor:completionHandler:)方法中赋值ShourtcutItem。
现在,我们已经将分接的快速操作分配给一个变量ShorkutItemToProcess。接下来,我们需要执行基于快捷方式项的操作。App struct内的.active开关中执行操作。修改App struct,如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var  body: some  Scene  {        WindowGroup  {             ListView ()         }         .onChange(of: phase) { (newPhase) in              switch  newPhase {             case  .active :                 print ("App in active" )                 guard  let  name =  shortcutItemToProcess? .userInfo? ["name" ] as?  String  else  {                     return                  }             case  .inactive:                                   print ("App is inactive" )             case  .background:                 print ("App in Back ground" )                 addQuickActions()             @unknown  default :                 print ("default" )             }         }     } 
从快捷方式的ItemToProcess变量中获取“name”值。(第10行)
导航到选定的快速操作视图 在本部分中,我们将在用户选择快速操作时导航相应的视图。为此,我们必须创建视图。
我已经创建了ListView,而不是ContentView。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import  SwiftUIstruct  ListView : View      @EnvironmentObject  var  quickActionSettings: QuickActionSettings      @State  var  selectedAction: Int ?          var  body: some  View  {         NavigationView  {             List  {                 ForEach (0 ..< allQuickActions.count) { index in                      NavigationLink (destination: DetailView (name: allQuickActions[index].name), tag: allQuickActions[index].tag, selection: $quickActionSettings .quickAction) {                         Text (allQuickActions[index].name)                     }                 }             }             .listStyle(SidebarListStyle ())             .navigationBarTitle("Quick Actions" )         }     } } struct  ListView_Previews : PreviewProvider      static  var  previews: some  View  {         ListView ()     } } struct  QuickActionModel  : Identifiable      let  id =  UUID ()     let  name: String      let  tag: QuickActionSettings .QuickAction  } let  allQuickActions =  [    QuickActionModel (name: "Contacts" , tag: .details(name: "contact" )),     QuickActionModel (name: "Chats" ,tag: .details(name: "chat" )),     QuickActionModel (name: "Calls" , tag: .details(name: "call" )),     QuickActionModel (name: "Status" , tag: .details(name: "status" )), ] 
我已经创建了简单的DetailView。
1 2 3 4 5 6 7 8 9 10 11 import  SwiftUIstruct  DetailView : View           var  name: String           var  body: some  View  {         Text ("\(name)  View" )             .navigationBarTitle(name)     } } 
和ObservableObject类QuickActionSettings
1 2 3 4 5 6 7 8 9 10 11 import  Foundationclass  QuickActionSettings : ObservableObject           enum  QuickAction : Hashable           case  home         case  details(name: String )     }          @Published  var  quickAction: QuickAction ? =  nil  } 
因此,逻辑是我们使用QuickActionSettings的QuickAction枚举作为ListView中NavigationLink的Selection:Value。如果QuickAction枚举值(IckAction)更改,那么NavigationLink将被激发,因为它是ListView中的绑定变量。
现在,我们需要做的就是根据选定的Quick Action更改Quick Action值。让我们执行App struct中的最后一步。
在App Struct外部为QuickActionSettings创建一个对象,该对象与ShorkutItemToProcess变量相同。
1 let  quickActionSettings =  QuickActionSettings ()
然后在App Struct的ListView中添加QuickActionSettings对象作为Environment Object。最终的App struct将如下所示,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 var  body: some  Scene  {        WindowGroup  {             ListView ()                 .environmentObject(quickActionSettings)         }         .onChange(of: phase) { (newPhase) in              switch  newPhase {             case  .active :                 print ("App in active" )                 guard  let  name =  shortcutItemToProcess? .userInfo? ["name" ] as?  String  else  {                     return                  }                 switch  name {                 case  "call" :                     print ("call is selected" )                     quickActionSettings.quickAction =  .details(name: name)                 case  "chat" :                     print ("chat is selected" )                     quickActionSettings.quickAction =  .details(name: name)                 case  "status" :                     print ("status is selected" )                     quickActionSettings.quickAction =  .details(name: name)                 case  "contact" :                     print ("contct is selected" )                     quickActionSettings.quickAction =  .details(name: name)                 default :                     print ("default " )                 }             case  .inactive:                                   print ("App is inactive" )             case  .background:                 print ("App in Back ground" )                 addQuickActions()             @unknown  default :                 print ("default" )             }         }     } 
在这里,当应用程序进入.active阶段时,我们分配的是ickAction值。这将触发ListView中的NavigationLink。
让我们构建并运行该应用程序。
项目地址 原作者仓库QuickActions 
备用仓库