ios - Dynamically edit 3d touch shortcut list
Asked Answered
V

4

8

I want to add a "Continue" shortcut to my game. But when user will finish my game completely I want this to be either removed or replaced by another shortcut. Is this possible? I know 3d touch is handled by ios system, but maybe there are still some options

Vachon answered 5/12, 2015 at 17:51 Comment(2)
Where is the continue shortcut placed? Also by shortcut I assume you mean button?Lidalidah
My game is level based. So this shortcut is UIApplicationShortCut (which appears in the main ios menu) and is intendent to launch last available levelVachon
S
11

There are two ways to create shortcuts - dynamic and static.

  • Static are added to the plist and never change.
  • Dynamic can be added and removed in code.

It sounds like you want a dynamic shortcut, so here's roughly how you would do that:

To add:

if #available(iOS 9.0, *) {
    if (UIApplication.sharedApplication().shortcutItems?.filter({ $0.type == "com.app.myshortcut" }).first == nil) {
        UIApplication.sharedApplication().shortcutItems?.append(UIMutableApplicationShortcutItem(type: "com.app.myshortcut", localizedTitle: "Shortcut Title"))
    }
}

To remove:

if #available(iOS 9.0, *) {
    if let shortcutItem = UIApplication.sharedApplication().shortcutItems?.filter({ $0.type == "com.app.myshortcut" }).first {
        let index = UIApplication.sharedApplication().shortcutItems?.indexOf(shortcutItem)

        UIApplication.sharedApplication().shortcutItems?.removeAtIndex(index!)
    }
}

You can then handle the shortcut by checking for it in the app delegate method:

@available(iOS 9.0, *)
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
    if shortcutItem.type == "com.app.myshortcut" {
        // Do something
    }
}

Don't forget to check for iOS9 and 3d Touch compatibility.

You can find Apple developer 3d touch pages here:

https://developer.apple.com/ios/3d-touch/

And specifically dynamic shortcuts here:

https://developer.apple.com/library/ios/samplecode/ApplicationShortcuts/Listings/ApplicationShortcuts_AppDelegate_swift.html#//apple_ref/doc/uid/TP40016545-ApplicationShortcuts_AppDelegate_swift-DontLinkElementID_3

Shandra answered 5/12, 2015 at 19:35 Comment(4)
Well it seems like it will fit my needs, the only sad part about it is that you can't set dynamic shortcuts after app is installed from AppStore. So I guess it is ok that my shortcuts will appear only after some time passes. Thank you for an answer, I will give it a try a little bit later and if it works I'll mark it as correct!Vachon
I agree, we have a good example with a login shortcut. It would be nice to show login from the point at which the app is downloaded and then remove this when the user logs in. Would be interested to hear if there is a solution but we haven't found one yet. Anyway, glad it helped and thanks for the feedbackShandra
has anyone tried this code? if shortcut items is not a mutable array, how do you remove straight from it? should you just create an empty array and set that instead?Season
@Season I think it is mutable. I can do this from my didFinishLaunchingWithOptions: application.shortcutItems?.append(myShortcut). When you do it let ..., of course, let will not be. How about if var shortcuts = UIApplication....Egon
O
4

Here's a handy class to trigger segues off of 3D touch off of your app icon. Of course you could trigger any action, but this is probably the most common. It then syncs itself when the app comes up or goes to the background. I'm using this to trigger a "My Projects" section only after the user has generated one (VisualizerProject.currentProject.images.count > 0).

class AppShortcut : UIMutableApplicationShortcutItem {
    var segue:String

    init(type:String, title:String, icon:String, segue:String) {
        self.segue = segue
        let translatedTitle = NSLocalizedString(title, comment:title)
        let iconImage = UIApplicationShortcutIcon(templateImageName: icon)
        super.init(type: type, localizedTitle:translatedTitle, localizedSubtitle:nil, icon:iconImage, userInfo:nil)
    }
}

class AppShortcuts {

    static var shortcuts:[AppShortcut] = []

    class func sync() {

        var newShortcuts:[AppShortcut] = []

        //reverse order for display
        newShortcuts.append(AppShortcut(type: "find-color", title: "Find Color", icon:"ic_settings_black_24px", segue: "showColorFinder"))

        newShortcuts.append(AppShortcut(type: "samples", title: "Sample Rooms", icon:"ic_photo_black_24px", segue: "showSamples"))

        //conditionally add an item like this:
        if (VisualizerProject.currentProject.images.count > 0) {
            newShortcuts.append(AppShortcut(type: "projects", title: "My Projects", icon:"ic_settings_black_24px", segue: "showProjects"))
        }

        newShortcuts.append(AppShortcut(type: "visualizer", title: "Paint Visualizer", icon:"ic_photo_camera_black_24px", segue: "showPainter"))

        UIApplication.sharedApplication().shortcutItems = newShortcuts
        shortcuts = newShortcuts
    }

    class func performShortcut(window:UIWindow, shortcut:UIApplicationShortcutItem) {

        sync()

        if let shortcutItem = shortcuts.filter({ $0.type == shortcut.type}).first {

            if let rootNavigationViewController = window.rootViewController as? UINavigationController,
                let landingViewController = rootNavigationViewController.viewControllers.first {
                //Pop to root view controller so that approperiete segue can be performed
                rootNavigationViewController.popToRootViewControllerAnimated(false)

                landingViewController.performSegueWithIdentifier(shortcutItem.segue, sender: self)
            }
        }
    }
}

Then in your app delegate, add the sync and perform shortcut calls

func applicationDidEnterBackground(application: UIApplication) {
    AppShortcuts.sync()
}

func applicationDidBecomeActive(application: UIApplication) {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

    AppShortcuts.sync()
}

@available(iOS 9.0, *)
func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {

    if let window = self.window {
         AppShortcuts.performShortcut(window, shortcut: shortcutItem)
    }
}
Olsewski answered 20/7, 2016 at 21:33 Comment(0)
I
0

I assume you're talking about the so-called Quick Actions a user can call by force-touching your app's icon on his home screen. You can dynamically create and update them right from your code. The best way to learn about all the possibilities is looking at Apple's sample code.

Ignatius answered 5/12, 2015 at 20:27 Comment(0)
S
0
 [UIApplication sharedApplication].shortcutItems = @[];

worked for me. the other answer that suggests removing something from the array didn't work as shortcutItems is not mutable.

Season answered 16/12, 2015 at 18:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.