简体   繁体   中英

Opening specific ViewController from Today Extension

I have got two buttons on my today extension. One is dedicated to open MainViewController and the second one need to navigate to second ViewController.

So I made a appURL:

   let appURL = NSURL(string: "StarterApplication://")

and then on a first button I call:

       self.extensionContext?.open(appURL! as URL, completionHandler:nil)

Which opens app on MainViewController.

How can I open MainViewController and performSegue to my SecondViewController when tapping second button on Today Widget?

I made a second URL scheme for that specific ViewController. I saw in other simmilar topics that it can be done by calling:

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
}

in AppDelegate but don't have any idea how to use it.

Use one URL scheme. You can add different paths or arguments for different task.

For example, I've an app that displays multiple items in a today extension. If you tap an item the app is opened.

- (void)_openItem:(SPItem *)item
{
    NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"myapp://localhost/open_item?itemID=%@", item.itemID]];
    if (url != nil)
    {
        [self.extensionContext openURL:url completionHandler:nil];
    }
}

As you've already mentioned in you question, you need to implement - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options

In my case it more or less looks like this:

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options
{
    BOOL ret;
    if ([url.scheme isEqualToString:@"myapp"])
    {
        if ([url.path isEqual:@"open_item"])
        {
            @try
            {
                NSDictionary *query = [url.query queryDictionary];
                NSString* itemID = query[@"itemID"];
                [self _gotoItemWithID:itemID completionHandler:nil];
            }
            @catch (NSException *exception) {
            }
            ret = YES;
        }
    }
    else
    {
        ret = NO;
    }
    return ret;
}

As you can see, I first check, if the url scheme. If it's the one I expect, I check the path. By using different paths I'm able to implement different commands the today extension is able to execute . Each command may have different arguments. In case of the "open_item" command, I expect the parameter "itemID".

By returning YES, you tell iOS you were able to handle the URL your app was called with.

In my app [self _gotoItemWithID:itemID completionHandler:nil] does all the need tasks to display the item. In your case you would need a function to display the second view controller.

Edit:

I forgot to mention that queryDictionary is a method in an NSString extension. It takes a string ( self ) and tries to extract URL parameter and return them as dictionary.

- (NSDictionary*)queryDictionary
{
    NSCharacterSet* delimiterSet = [NSCharacterSet characterSetWithCharactersInString:@"&;"];
    NSMutableDictionary* pairs = [NSMutableDictionary dictionary];
    NSScanner* scanner = [[NSScanner alloc] initWithString:self];
    while (![scanner isAtEnd])
    {
        NSString* pairString;
        [scanner scanUpToCharactersFromSet:delimiterSet
                                intoString:&pairString] ;
        [scanner scanCharactersFromSet:delimiterSet intoString:NULL];
        NSArray* kvPair = [pairString componentsSeparatedByString:@"="];
        if ([kvPair count] == 2)
        {
            NSString* key = [[kvPair objectAtIndex:0] stringByRemovingPercentEncoding];
            NSString* value = [[kvPair objectAtIndex:1] stringByRemovingPercentEncoding];
            [pairs setObject:value forKey:key] ;
        }
    }
    return [NSDictionary dictionaryWithDictionary:pairs];
}

I found the solution. The answer is deep linking. Here is my method in appDelegate:

    func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {


    let urlPath : String = url.path as String!
    let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)

    if(urlPath == "/Action"){

        let ActionView: ActionViewController = mainStoryboard.instantiateViewController(withIdentifier: "ActionViewController") as! ActionViewController
        self.window?.rootViewController = ActionView
    } else if(urlPath == "/VoiceCommandView") {

        let VoiceCommandView: ViewController = mainStoryboard.instantiateViewController(withIdentifier: "ViewController") as! ViewController
        self.window?.rootViewController = VoiceCommandView

    }
    self.window?.makeKeyAndVisible()

    return true

And in the TodayExtensionViewController I defined two URL schemes with same host but different URL paths. And made a simple:

        self.extensionContext?.open(startAppURL! as URL, completionHandler:nil)

for first button but changed the urlPath for the second button.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM