Search code examples
swiftappdelegate3dtouch

Warning: Attempt to Present ViewController on whose ViewController isn't in the view hierarchy (3D Touch)


Basically, I am trying to implement Home Screen Quick Actions into my app. When I tap on one of the quick actions I get the error:

Warning: Attempt to present theViewController on ViewController whose view is not in the window hierarchy!

I have looked at some other Stack Over Flow posts that had the same issue, but the solutions didn't work for me. Also, in my applicationDidEnterBackground method I added the self.window?.rootViewController?.dismissViewControllerAnimated(true, completion: nil).

Here are some of the relevant 3D Touch methods that I have included in the App Delegate:

func application(application: UIApplication, performActionForShortcutItem shortcutItem: UIApplicationShortcutItem, completionHandler: (Bool) -> Void) {
    let handledShortcutItem = self.handleShortuctItem(shortcutItem)
    completionHandler(handledShortcutItem)
}

Also, I have these helper methods:

    enum ShortcutIdentifier: String {
    case First
    case Second

    init?(fullType: String) {
        guard let last = fullType.componentsSeparatedByString(".").last else { return nil }
        self.init(rawValue: last)
    }
    var type: String {
        return NSBundle.mainBundle().bundleIdentifier! + ".\(self.rawValue)"
    }
}

func handleShortuctItem(shortcutItem: UIApplicationShortcutItem) -> Bool {
    var handled = false

    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:
        handled = true
        let navVC = mainStoryboard.instantiateViewControllerWithIdentifier("firstViewController") as! FirstViewController
        self.window?.rootViewController?.presentViewController(navVC, animated: true, completion: nil)
        break
    case ShortcutIdentifier.Second.type:
        handled = true
        let navVC = mainStoryboard.instantiateViewControllerWithIdentifier("secondViewController") as! SecondViewController
        self.window?.rootViewController?.presentViewController(navVC, animated: true, completion: nil)
        break
    default:
        break
    }


    return handled
}

Solution

  • A sort of mindless solution is to wrap a delay around your presentViewController calls (delay is defined here: dispatch_after - GCD in swift?):

    switch(shortcutType) {
    case ShortcutIdentifier.First.type:
        handled = true
        let navVC = mainStoryboard.instantiateViewControllerWithIdentifier("firstViewController") as! FirstViewController
        delay(0.3) {
            self.window?.rootViewController?.presentViewController(navVC, animated: true, completion: nil)
        }
        break
    case ShortcutIdentifier.Second.type:
        handled = true
        let navVC = mainStoryboard.instantiateViewControllerWithIdentifier("secondViewController") as! SecondViewController
        delay(0.3) {
            self.window?.rootViewController?.presentViewController(navVC, animated: true, completion: nil)
        }
        break
    default:
        break
    }
    

    The idea is to give the interface time to finish appearing before trying to do the presentation.