From c580342ab1c7abb01d7ace21daffa8bb6c07e95a Mon Sep 17 00:00:00 2001 From: Liu Lantao Date: Mon, 20 Nov 2017 07:48:58 +0800 Subject: [PATCH] ApplicationShortcuts: Version 1.2, 2016-09-28 Upgraded to Swift 2.3. Demonstrates how to use the UIApplicationShortcutItem class to provide quick access to parts of your application directly from the device's home screen. The sample shows two static shortcuts (defined in the app's Info.plist), and two dynamic shortcuts (defined in code with the UIMutableApplicationShortcutItem class). The dynamic shortcuts can be edited to change the title, subtitle and icon. Signed-off-by: Liu Lantao --- .../project.pbxproj | 332 ++++++++++++++++++ .../ApplicationShortcuts/AppDelegate.swift | 147 ++++++++ .../AppIcon.appiconset/Contents.json | 38 ++ .../Assets.xcassets/Contents.json | 6 + .../Base.lproj/InfoPlist.strings | 5 + .../Base.lproj/LaunchScreen.storyboard | 44 +++ .../Base.lproj/Main.storyboard | 199 +++++++++++ .../ApplicationShortcuts/Info.plist | 83 +++++ .../ShortcutDetailViewController.swift | 115 ++++++ .../ShortcutsTableViewController.swift | 126 +++++++ .../en.lproj/InfoPlist.strings | 5 + ApplicationShortcuts/LICENSE.txt | 42 +++ ApplicationShortcuts/README.md | 20 ++ 13 files changed, 1162 insertions(+) create mode 100755 ApplicationShortcuts/ApplicationShortcuts.xcodeproj/project.pbxproj create mode 100755 ApplicationShortcuts/ApplicationShortcuts/AppDelegate.swift create mode 100755 ApplicationShortcuts/ApplicationShortcuts/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100755 ApplicationShortcuts/ApplicationShortcuts/Assets.xcassets/Contents.json create mode 100644 ApplicationShortcuts/ApplicationShortcuts/Base.lproj/InfoPlist.strings create mode 100755 ApplicationShortcuts/ApplicationShortcuts/Base.lproj/LaunchScreen.storyboard create mode 100755 ApplicationShortcuts/ApplicationShortcuts/Base.lproj/Main.storyboard create mode 100755 ApplicationShortcuts/ApplicationShortcuts/Info.plist create mode 100755 ApplicationShortcuts/ApplicationShortcuts/ShortcutDetailViewController.swift create mode 100755 ApplicationShortcuts/ApplicationShortcuts/ShortcutsTableViewController.swift create mode 100644 ApplicationShortcuts/ApplicationShortcuts/en.lproj/InfoPlist.strings create mode 100644 ApplicationShortcuts/LICENSE.txt create mode 100644 ApplicationShortcuts/README.md diff --git a/ApplicationShortcuts/ApplicationShortcuts.xcodeproj/project.pbxproj b/ApplicationShortcuts/ApplicationShortcuts.xcodeproj/project.pbxproj new file mode 100755 index 00000000..81569291 --- /dev/null +++ b/ApplicationShortcuts/ApplicationShortcuts.xcodeproj/project.pbxproj @@ -0,0 +1,332 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 5345AE1C1B97709700F65A71 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5345AE1E1B97709700F65A71 /* InfoPlist.strings */; }; + 53A5377E1B8CEC2600CBD3C3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A537701B8CEC2600CBD3C3 /* AppDelegate.swift */; }; + 53A5377F1B8CEC2600CBD3C3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 53A537711B8CEC2600CBD3C3 /* Assets.xcassets */; }; + 53A537801B8CEC2600CBD3C3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53A537731B8CEC2600CBD3C3 /* LaunchScreen.storyboard */; }; + 53A537811B8CEC2600CBD3C3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53A537751B8CEC2600CBD3C3 /* Main.storyboard */; }; + 53A537871B8CEC2600CBD3C3 /* ShortcutDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A5377C1B8CEC2600CBD3C3 /* ShortcutDetailViewController.swift */; }; + 53A537881B8CEC2600CBD3C3 /* ShortcutsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A5377D1B8CEC2600CBD3C3 /* ShortcutsTableViewController.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 5345AE1D1B97709700F65A71 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/InfoPlist.strings; sourceTree = ""; }; + 5345AE1F1B97709B00F65A71 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 53A537701B8CEC2600CBD3C3 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = ApplicationShortcuts/AppDelegate.swift; sourceTree = SOURCE_ROOT; }; + 53A537711B8CEC2600CBD3C3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ApplicationShortcuts/Assets.xcassets; sourceTree = SOURCE_ROOT; }; + 53A537741B8CEC2600CBD3C3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = LaunchScreen.storyboard; sourceTree = ""; }; + 53A537761B8CEC2600CBD3C3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Main.storyboard; sourceTree = ""; }; + 53A537791B8CEC2600CBD3C3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ApplicationShortcuts/Info.plist; sourceTree = SOURCE_ROOT; }; + 53A5377C1B8CEC2600CBD3C3 /* ShortcutDetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShortcutDetailViewController.swift; path = ApplicationShortcuts/ShortcutDetailViewController.swift; sourceTree = SOURCE_ROOT; }; + 53A5377D1B8CEC2600CBD3C3 /* ShortcutsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ShortcutsTableViewController.swift; path = ApplicationShortcuts/ShortcutsTableViewController.swift; sourceTree = SOURCE_ROOT; }; + EB7B7F951B8645D30063172E /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + EBD4FAAF1B83ED9C009B2114 /* AppShortcuts.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppShortcuts.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + EBD4FAAC1B83ED9C009B2114 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 53A537721B8CEC2600CBD3C3 /* Base.lproj */ = { + isa = PBXGroup; + children = ( + 5345AE1E1B97709700F65A71 /* InfoPlist.strings */, + 53A537731B8CEC2600CBD3C3 /* LaunchScreen.storyboard */, + 53A537751B8CEC2600CBD3C3 /* Main.storyboard */, + ); + name = Base.lproj; + path = ApplicationShortcuts/Base.lproj; + sourceTree = SOURCE_ROOT; + }; + EBD4FAA61B83ED9C009B2114 = { + isa = PBXGroup; + children = ( + EB7B7F951B8645D30063172E /* README.md */, + EBD4FAB11B83ED9D009B2114 /* ApplicationShortcuts */, + EBD4FAB01B83ED9D009B2114 /* Products */, + ); + sourceTree = ""; + }; + EBD4FAB01B83ED9D009B2114 /* Products */ = { + isa = PBXGroup; + children = ( + EBD4FAAF1B83ED9C009B2114 /* AppShortcuts.app */, + ); + name = Products; + sourceTree = ""; + }; + EBD4FAB11B83ED9D009B2114 /* ApplicationShortcuts */ = { + isa = PBXGroup; + children = ( + 53A537701B8CEC2600CBD3C3 /* AppDelegate.swift */, + 53A5377D1B8CEC2600CBD3C3 /* ShortcutsTableViewController.swift */, + 53A5377C1B8CEC2600CBD3C3 /* ShortcutDetailViewController.swift */, + 53A537791B8CEC2600CBD3C3 /* Info.plist */, + 53A537711B8CEC2600CBD3C3 /* Assets.xcassets */, + 53A537721B8CEC2600CBD3C3 /* Base.lproj */, + ); + name = ApplicationShortcuts; + path = Tabs; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + EBD4FAAE1B83ED9C009B2114 /* ApplicationShortcuts */ = { + isa = PBXNativeTarget; + buildConfigurationList = EBD4FAC31B83ED9D009B2114 /* Build configuration list for PBXNativeTarget "ApplicationShortcuts" */; + buildPhases = ( + EBD4FAAB1B83ED9C009B2114 /* Sources */, + EBD4FAAC1B83ED9C009B2114 /* Frameworks */, + EBD4FAAD1B83ED9C009B2114 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ApplicationShortcuts; + productName = Tabs; + productReference = EBD4FAAF1B83ED9C009B2114 /* AppShortcuts.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + EBD4FAA71B83ED9C009B2114 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0700; + LastUpgradeCheck = 0800; + ORGANIZATIONNAME = Apple; + TargetAttributes = { + EBD4FAAE1B83ED9C009B2114 = { + CreatedOnToolsVersion = 7.0; + LastSwiftMigration = 0800; + }; + }; + }; + buildConfigurationList = EBD4FAAA1B83ED9C009B2114 /* Build configuration list for PBXProject "ApplicationShortcuts" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = EBD4FAA61B83ED9C009B2114; + productRefGroup = EBD4FAB01B83ED9D009B2114 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + EBD4FAAE1B83ED9C009B2114 /* ApplicationShortcuts */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + EBD4FAAD1B83ED9C009B2114 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53A537811B8CEC2600CBD3C3 /* Main.storyboard in Resources */, + 5345AE1C1B97709700F65A71 /* InfoPlist.strings in Resources */, + 53A5377F1B8CEC2600CBD3C3 /* Assets.xcassets in Resources */, + 53A537801B8CEC2600CBD3C3 /* LaunchScreen.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + EBD4FAAB1B83ED9C009B2114 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53A537871B8CEC2600CBD3C3 /* ShortcutDetailViewController.swift in Sources */, + 53A5377E1B8CEC2600CBD3C3 /* AppDelegate.swift in Sources */, + 53A537881B8CEC2600CBD3C3 /* ShortcutsTableViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 5345AE1E1B97709700F65A71 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 5345AE1D1B97709700F65A71 /* Base */, + 5345AE1F1B97709B00F65A71 /* en */, + ); + name = InfoPlist.strings; + path = ApplicationShortcuts; + sourceTree = SOURCE_ROOT; + }; + 53A537731B8CEC2600CBD3C3 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 53A537741B8CEC2600CBD3C3 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; + 53A537751B8CEC2600CBD3C3 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 53A537761B8CEC2600CBD3C3 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + EBD4FAC11B83ED9D009B2114 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + EBD4FAC21B83ED9D009B2114 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + EBD4FAC41B83ED9D009B2114 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + INFOPLIST_FILE = ApplicationShortcuts/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.ApplicationShortcuts"; + PRODUCT_NAME = AppShortcuts; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 2.3; + }; + name = Debug; + }; + EBD4FAC51B83ED9D009B2114 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + INFOPLIST_FILE = ApplicationShortcuts/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.ApplicationShortcuts"; + PRODUCT_NAME = AppShortcuts; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 2.3; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + EBD4FAAA1B83ED9C009B2114 /* Build configuration list for PBXProject "ApplicationShortcuts" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EBD4FAC11B83ED9D009B2114 /* Debug */, + EBD4FAC21B83ED9D009B2114 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + EBD4FAC31B83ED9D009B2114 /* Build configuration list for PBXNativeTarget "ApplicationShortcuts" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EBD4FAC41B83ED9D009B2114 /* Debug */, + EBD4FAC51B83ED9D009B2114 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = EBD4FAA71B83ED9C009B2114 /* Project object */; +} diff --git a/ApplicationShortcuts/ApplicationShortcuts/AppDelegate.swift b/ApplicationShortcuts/ApplicationShortcuts/AppDelegate.swift new file mode 100755 index 00000000..8a42c099 --- /dev/null +++ b/ApplicationShortcuts/ApplicationShortcuts/AppDelegate.swift @@ -0,0 +1,147 @@ +/* + Copyright (C) 2016 Apple Inc. All Rights Reserved. + See LICENSE.txt for this sample’s licensing information + + Abstract: + The application delegate class used to manage this sample. +*/ + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + // MARK: - Types + + enum ShortcutIdentifier: String { + case First + case Second + case Third + case Fourth + + // MARK: - Initializers + + init?(fullType: String) { + guard let last = fullType.componentsSeparatedByString(".").last else { return nil } + + self.init(rawValue: last) + } + + // MARK: - Properties + + var type: String { + return NSBundle.mainBundle().bundleIdentifier! + ".\(self.rawValue)" + } + } + + // MARK: - Static Properties + + static let applicationShortcutUserInfoIconKey = "applicationShortcutUserInfoIconKey" + + // MARK: - Properties + + /* + The app delegate must implement the window from UIApplicationDelegate + protocol to use a main storyboard file. + */ + var window: UIWindow? + + /// Saved shortcut item used as a result of an app launch, used later when app is activated. + var launchedShortcutItem: UIApplicationShortcutItem? + + func handleShortCutItem(shortcutItem: UIApplicationShortcutItem) -> Bool { + var handled = false + + // Verify that the provided `shortcutItem`'s `type` is one handled by the application. + guard ShortcutIdentifier(fullType: shortcutItem.type) != nil else { return false } + + guard let shortCutType = shortcutItem.type as String? else { return false } + + switch (shortCutType) { + case ShortcutIdentifier.First.type: + // Handle shortcut 1 (static). + handled = true + break + case ShortcutIdentifier.Second.type: + // Handle shortcut 2 (static). + handled = true + break + case ShortcutIdentifier.Third.type: + // Handle shortcut 3 (dynamic). + handled = true + break + case ShortcutIdentifier.Fourth.type: + // Handle shortcut 4 (dynamic). + handled = true + break + default: + break + } + + // Construct an alert using the details of the shortcut used to open the application. + let alertController = UIAlertController(title: "Shortcut Handled", message: "\"\(shortcutItem.localizedTitle)\"", preferredStyle: .Alert) + let okAction = UIAlertAction(title: "OK", style: .Default, handler: nil) + alertController.addAction(okAction) + + // Display an alert indicating the shortcut selected from the home screen. + window!.rootViewController?.presentViewController(alertController, animated: true, completion: nil) + + return handled + } + + // MARK: - Application Life Cycle + + func applicationDidBecomeActive(application: UIApplication) { + guard let shortcut = launchedShortcutItem else { return } + + handleShortCutItem(shortcut) + + launchedShortcutItem = nil + } + + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + + // Override point for customization after application launch. + var shouldPerformAdditionalDelegateHandling = true + + // If a shortcut was launched, display its information and take the appropriate action + if let shortcutItem = launchOptions?[UIApplicationLaunchOptionsShortcutItemKey] as? UIApplicationShortcutItem { + + launchedShortcutItem = shortcutItem + + // This will block "performActionForShortcutItem:completionHandler" from being called. + shouldPerformAdditionalDelegateHandling = false + } + + // Install initial versions of our two extra dynamic shortcuts. + if let shortcutItems = application.shortcutItems where shortcutItems.isEmpty { + // Construct the items. + let shortcut3 = UIMutableApplicationShortcutItem(type: ShortcutIdentifier.Third.type, localizedTitle: "Play", localizedSubtitle: "Will Play an item", icon: UIApplicationShortcutIcon(type: .Play), userInfo: [ + AppDelegate.applicationShortcutUserInfoIconKey: UIApplicationShortcutIconType.Play.rawValue + ] + ) + + let shortcut4 = UIMutableApplicationShortcutItem(type: ShortcutIdentifier.Fourth.type, localizedTitle: "Pause", localizedSubtitle: "Will Pause an item", icon: UIApplicationShortcutIcon(type: .Pause), userInfo: [ + AppDelegate.applicationShortcutUserInfoIconKey: UIApplicationShortcutIconType.Pause.rawValue + ] + ) + + // Update the application providing the initial 'dynamic' shortcut items. + application.shortcutItems = [shortcut3, shortcut4] + } + + return shouldPerformAdditionalDelegateHandling + } + + /* + Called when the user activates your application by selecting a shortcut on the home screen, except when + application(_:,willFinishLaunchingWithOptions:) or application(_:didFinishLaunchingWithOptions) returns `false`. + You should handle the shortcut in those callbacks and return `false` if possible. In that case, this + callback is used if your application is already launched in the background. + */ + func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: Bool -> Void) { + let handledShortCutItem = handleShortCutItem(shortcutItem) + + completionHandler(handledShortCutItem) + } +} diff --git a/ApplicationShortcuts/ApplicationShortcuts/Assets.xcassets/AppIcon.appiconset/Contents.json b/ApplicationShortcuts/ApplicationShortcuts/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100755 index 00000000..118c98f7 --- /dev/null +++ b/ApplicationShortcuts/ApplicationShortcuts/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ApplicationShortcuts/ApplicationShortcuts/Assets.xcassets/Contents.json b/ApplicationShortcuts/ApplicationShortcuts/Assets.xcassets/Contents.json new file mode 100755 index 00000000..da4a164c --- /dev/null +++ b/ApplicationShortcuts/ApplicationShortcuts/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ApplicationShortcuts/ApplicationShortcuts/Base.lproj/InfoPlist.strings b/ApplicationShortcuts/ApplicationShortcuts/Base.lproj/InfoPlist.strings new file mode 100644 index 00000000..2b028259 --- /dev/null +++ b/ApplicationShortcuts/ApplicationShortcuts/Base.lproj/InfoPlist.strings @@ -0,0 +1,5 @@ +shortcutTitle1 = "Search"; +shortcutSubtitle1 = "Will search for an item"; + +shortcutTitle2 = "Share"; +shortcutSubtitle2 = "Will share an item"; \ No newline at end of file diff --git a/ApplicationShortcuts/ApplicationShortcuts/Base.lproj/LaunchScreen.storyboard b/ApplicationShortcuts/ApplicationShortcuts/Base.lproj/LaunchScreen.storyboard new file mode 100755 index 00000000..495d6c28 --- /dev/null +++ b/ApplicationShortcuts/ApplicationShortcuts/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ApplicationShortcuts/ApplicationShortcuts/Base.lproj/Main.storyboard b/ApplicationShortcuts/ApplicationShortcuts/Base.lproj/Main.storyboard new file mode 100755 index 00000000..d21533bb --- /dev/null +++ b/ApplicationShortcuts/ApplicationShortcuts/Base.lproj/Main.storyboard @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ApplicationShortcuts/ApplicationShortcuts/Info.plist b/ApplicationShortcuts/ApplicationShortcuts/Info.plist new file mode 100755 index 00000000..5a48bc1f --- /dev/null +++ b/ApplicationShortcuts/ApplicationShortcuts/Info.plist @@ -0,0 +1,83 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.2 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationShortcutItems + + + UIApplicationShortcutItemIconType + UIApplicationShortcutIconTypeSearch + UIApplicationShortcutItemSubtitle + shortcutSubtitle1 + UIApplicationShortcutItemTitle + shortcutTitle1 + UIApplicationShortcutItemType + $(PRODUCT_BUNDLE_IDENTIFIER).First + UIApplicationShortcutItemUserInfo + + firstShorcutKey1 + firstShortcutKeyValue1 + + + + UIApplicationShortcutItemIconType + UIApplicationShortcutIconTypeShare + UIApplicationShortcutItemSubtitle + shortcutSubtitle2 + UIApplicationShortcutItemTitle + shortcutTitle2 + UIApplicationShortcutItemType + $(PRODUCT_BUNDLE_IDENTIFIER).Second + UIApplicationShortcutItemUserInfo + + secondShortcutKey1 + secondShortcutValue1 + + + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UIStatusBarTintParameters + + UINavigationBar + + Style + UIBarStyleDefault + Translucent + + + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/ApplicationShortcuts/ApplicationShortcuts/ShortcutDetailViewController.swift b/ApplicationShortcuts/ApplicationShortcuts/ShortcutDetailViewController.swift new file mode 100755 index 00000000..2548620f --- /dev/null +++ b/ApplicationShortcuts/ApplicationShortcuts/ShortcutDetailViewController.swift @@ -0,0 +1,115 @@ +/* + Copyright (C) 2016 Apple Inc. All Rights Reserved. + See LICENSE.txt for this sample’s licensing information + + Abstract: + The detail view controller for viewing and editing shortcut information (localized title, localized subtitle, and icon) +*/ + +import UIKit + +class ShortcutDetailViewController: UITableViewController { + + // MARK: - Properties + + @IBOutlet weak var titleTextField: UITextField! + @IBOutlet weak var subtitleTextField: UITextField! + @IBOutlet weak var pickerView: UIPickerView! + @IBOutlet weak var doneButton: UIBarButtonItem! + + var pickerItems = ["Compose", "Play", "Pause", "Add", "Location", "Search", "Share"] + + /// Used to share information between this controller and its parent. + var shortcutItem: UIApplicationShortcutItem? + + /// The observer token for the `UITextFieldDidChangeNotification`. + var textFieldObserverToken: NSObjectProtocol? + + // MARK: - Object Life Cycle + + deinit { + guard let token = textFieldObserverToken else { return } + NSNotificationCenter.defaultCenter().removeObserver(token) + } + + // MARK: - View Life Cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Initialize the UI to reflect the values of the `shortcutItem`. + guard let selectedShortcutItem = shortcutItem else { + fatalError("The `selectedShortcutItem` was not set.") + } + + title = selectedShortcutItem.localizedTitle + + titleTextField.text = selectedShortcutItem.localizedTitle + subtitleTextField.text = selectedShortcutItem.localizedSubtitle + + // Extract the raw value representing the icon from the userInfo dictionary, if provided. + guard let iconRawValue = selectedShortcutItem.userInfo?[AppDelegate.applicationShortcutUserInfoIconKey] as? Int else { return } + + // Select the matching row in the picker for the icon type. + let iconType = iconTypeForSelectedRow(iconRawValue) + + // The `iconType` returned may not align to the `iconRawValue` so use the `iconType`'s `rawValue`. + pickerView.selectRow(iconType.rawValue, inComponent:0, animated:false) + + let notificationCenter = NSNotificationCenter.defaultCenter() + textFieldObserverToken = notificationCenter.addObserverForName(UITextFieldTextDidChangeNotification, object: nil, queue: nil) { [weak self] _ in + guard let strongSelf = self else { return } + + // You cannot dismiss the view controller without a valid shortcut title. + let titleTextLength = strongSelf.titleTextField.text?.characters.count ?? 0 + strongSelf.doneButton.enabled = titleTextLength > 0 + } + } + + // MARK: - UITextFieldDelegate + + func textFieldShouldReturn(textField: UITextField) -> Bool { + textField.resignFirstResponder() + + return true + } + + // MARK: - UIPickerViewDataSource + + func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int { + return 1 + } + + func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + return pickerItems.count + } + + func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { + return pickerItems[row] + } + + /// Constructs a UIApplicationShortcutIconType based on the integer result from our picker. + func iconTypeForSelectedRow(row: Int) -> UIApplicationShortcutIconType { + return UIApplicationShortcutIconType(rawValue: row) ?? .Compose + } + + // MARK: - UIStoryboardSegue Handling + + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + guard let selectedShortcutItem = shortcutItem else { + fatalError("The `selectedShortcutItem` was not set.") + } + + if segue.identifier == "ShortcutDetailUpdated" { + // In the updated case, create a shortcut item to represent the final state of the view controller. + let iconType = iconTypeForSelectedRow(pickerView.selectedRowInComponent(0)) + + let icon = UIApplicationShortcutIcon(type: iconType) + + shortcutItem = UIApplicationShortcutItem(type: selectedShortcutItem.type, localizedTitle: titleTextField.text ?? "", localizedSubtitle: subtitleTextField.text, icon: icon, userInfo: [ + AppDelegate.applicationShortcutUserInfoIconKey: pickerView.selectedRowInComponent(0) + ] + ) + } + } +} diff --git a/ApplicationShortcuts/ApplicationShortcuts/ShortcutsTableViewController.swift b/ApplicationShortcuts/ApplicationShortcuts/ShortcutsTableViewController.swift new file mode 100755 index 00000000..97d28424 --- /dev/null +++ b/ApplicationShortcuts/ApplicationShortcuts/ShortcutsTableViewController.swift @@ -0,0 +1,126 @@ +/* + Copyright (C) 2016 Apple Inc. All Rights Reserved. + See LICENSE.txt for this sample’s licensing information + + Abstract: + The primary view controller listing the mutable shortcuts registered for this sample. +*/ + +import UIKit + +class ShortcutsTableViewController: UITableViewController { + // MARK: - Properties + + /// Pre-defined shortcuts; retrieved from the Info.plist, lazily. + lazy var staticShortcuts: [UIApplicationShortcutItem] = { + // Obtain the `UIApplicationShortcutItems` array from the Info.plist. If unavailable, there are no static shortcuts. + guard let shortcuts = NSBundle.mainBundle().infoDictionary?["UIApplicationShortcutItems"] as? [[String: NSObject]] else { return [] } + + // Use `flatMap(_:)` to process each dictionary into a `UIApplicationShortcutItem`, if possible. + let shortcutItems = shortcuts.flatMap { shortcut -> [UIApplicationShortcutItem] in + // The `UIApplicationShortcutItemType` and `UIApplicationShortcutItemTitle` keys are required to successfully create a `UIApplicationShortcutItem`. + guard let shortcutType = shortcut["UIApplicationShortcutItemType"] as? String, + let shortcutTitle = shortcut["UIApplicationShortcutItemTitle"] as? String else { return [] } + + // Get the localized title. + var localizedShortcutTitle = shortcutTitle + if let localizedTitle = NSBundle.mainBundle().localizedInfoDictionary?[shortcutTitle] as? String { + localizedShortcutTitle = localizedTitle + } + + /* + The `UIApplicationShortcutItemSubtitle` key is optional. If it + exists, get the localized version. + */ + var localizedShortcutSubtitle: String? + if let shortcutSubtitle = shortcut["UIApplicationShortcutItemSubtitle"] as? String { + localizedShortcutSubtitle = NSBundle.mainBundle().localizedInfoDictionary?[shortcutSubtitle] as? String + } + + return [ + UIApplicationShortcutItem(type: shortcutType, localizedTitle: localizedShortcutTitle, localizedSubtitle: localizedShortcutSubtitle, icon: nil, userInfo: nil) + ] + } + + return shortcutItems + }() + + /// Shortcuts defined by the application and modifiable based on application state. + lazy var dynamicShortcuts = UIApplication.sharedApplication().shortcutItems ?? [] + + // MARK: - UITableViewDataSource + + override func numberOfSectionsInTableView(tableView: UITableView) -> Int { + return 2 + } + + override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return ["Static", "Dynamic"][section] + } + + override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return section == 0 ? staticShortcuts.count : dynamicShortcuts.count + } + + override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCellWithIdentifier("CellID", forIndexPath: indexPath) + + let shortcut: UIApplicationShortcutItem + + if indexPath.section == 0 { + // Static shortcuts (cannot be edited). + shortcut = staticShortcuts[indexPath.row] + cell.accessoryType = .None + cell.selectionStyle = .None + } + else { + // Dynamic shortcuts. + shortcut = dynamicShortcuts[indexPath.row] + } + + cell.textLabel?.text = shortcut.localizedTitle + cell.detailTextLabel?.text = shortcut.localizedSubtitle + + return cell + } + + // MARK: - Navigation + + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + // Supply the `shortcutItem` matching the selected row from the data source. + if segue.identifier == "ShowShortcutDetail" { + guard let indexPath = tableView.indexPathForSelectedRow, + let controller = segue.destinationViewController as? ShortcutDetailViewController else { return } + + controller.shortcutItem = dynamicShortcuts[indexPath.row] + } + } + + override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool { + // Block navigating to detail view controller for static shortcuts (which are not editable). + guard let selectedIndexPath = tableView.indexPathForSelectedRow else { return false } + + return selectedIndexPath.section > 0 + } + + // MARK: - Actions + + // Unwind segue action called when the user taps 'Done' after navigating to the detail controller. + @IBAction func done(sender: UIStoryboardSegue) { + // Obtain the edited shortcut from our source view controller. + guard let sourceViewController = sender.sourceViewController as? ShortcutDetailViewController, + let selected = tableView.indexPathForSelectedRow, + let updatedShortcutItem = sourceViewController.shortcutItem else { return } + + // Update our data source. + dynamicShortcuts[selected.row] = updatedShortcutItem + + // Update the application's `shortcutItems`. + UIApplication.sharedApplication().shortcutItems = dynamicShortcuts + + tableView.reloadRowsAtIndexPaths([selected], withRowAnimation: .Automatic) + } + + // Unwind segue action called when the user taps 'Cancel' after navigating to the detail controller. + @IBAction func cancel(sender: UIStoryboardSegue) {} +} diff --git a/ApplicationShortcuts/ApplicationShortcuts/en.lproj/InfoPlist.strings b/ApplicationShortcuts/ApplicationShortcuts/en.lproj/InfoPlist.strings new file mode 100644 index 00000000..2b028259 --- /dev/null +++ b/ApplicationShortcuts/ApplicationShortcuts/en.lproj/InfoPlist.strings @@ -0,0 +1,5 @@ +shortcutTitle1 = "Search"; +shortcutSubtitle1 = "Will search for an item"; + +shortcutTitle2 = "Share"; +shortcutSubtitle2 = "Will share an item"; \ No newline at end of file diff --git a/ApplicationShortcuts/LICENSE.txt b/ApplicationShortcuts/LICENSE.txt new file mode 100644 index 00000000..59676f7d --- /dev/null +++ b/ApplicationShortcuts/LICENSE.txt @@ -0,0 +1,42 @@ +Sample code project: ApplicationShortcuts: Using UIApplicationShortcutItem +Version: 1.2 + +IMPORTANT: This Apple software is supplied to you by Apple +Inc. ("Apple") in consideration of your agreement to the following +terms, and your use, installation, modification or redistribution of +this Apple software constitutes acceptance of these terms. If you do +not agree with these terms, please do not use, install, modify or +redistribute this Apple software. + +In consideration of your agreement to abide by the following terms, and +subject to these terms, Apple grants you a personal, non-exclusive +license, under Apple's copyrights in this original Apple software (the +"Apple Software"), to use, reproduce, modify and redistribute the Apple +Software, with or without modifications, in source and/or binary forms; +provided that if you redistribute the Apple Software in its entirety and +without modifications, you must retain this notice and the following +text and disclaimers in all such redistributions of the Apple Software. +Neither the name, trademarks, service marks or logos of Apple Inc. may +be used to endorse or promote products derived from the Apple Software +without specific prior written permission from Apple. Except as +expressly stated in this notice, no other rights or licenses, express or +implied, are granted by Apple herein, including but not limited to any +patent rights that may be infringed by your derivative works or by other +works in which the Apple Software may be incorporated. + +The Apple Software is provided by Apple on an "AS IS" basis. APPLE +MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION +THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND +OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + +IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, +MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED +AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), +STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +Copyright (C) 2016 Apple Inc. All Rights Reserved. diff --git a/ApplicationShortcuts/README.md b/ApplicationShortcuts/README.md new file mode 100644 index 00000000..38905362 --- /dev/null +++ b/ApplicationShortcuts/README.md @@ -0,0 +1,20 @@ +# Application Shortcuts: Using UIApplicationShortcutItem + +## Summary + +Demonstrates how to use the UIApplicationShortcutItem class in your application so users can quickly access parts of an application through “Quick actions” from the Home screen. +It implements four different shortcuts, two static defined (UIApplicationShortcutItem) in your Info.pist, two dynamic or mutable (UIMutableApplicationShortcutItem) defined through code. The mutable shortcuts can be edited to change that title, subtitle and icon. + +IMPORTANT: This sample should be run on an iPhone 6s or 6s Plus device. The iOS simulator will allow use of the shortcuts through its Hardware -> Touch Pressure menu, which requires a Trackpad. + +## Requirements + +### Build + +iOS 10.0 SDK, Xcode 8.0 + +### Runtime + +iOS 9.0 + +Copyright (C) 2015-2016 Apple Inc. All rights reserved.