17

我想启动我的包含应用程序。

我尝试使用 URL 方案。

URL 方案从其他地方启动了应用程序 - 所以问题不存在。

看起来这个对象是零:

   self.extensionContext

因此我无法运行此方法:

[self.extensionContext openURL:url completionHandler:nil];

我可以启动我的应用程序吗?URL Schemes 可以在自定义键盘中使用吗?

谢谢!

4

11 回答 11

15

试试这个代码

    UIResponder* responder = self;
    while ((responder = [responder nextResponder]) != nil)
    {
        NSLog(@"responder = %@", responder);
        if([responder respondsToSelector:@selector(openURL:)] == YES)
        {
            [responder performSelector:@selector(openURL:) withObject:[NSURL URLWithString:urlString]];
        }
    }
于 2015-04-10T06:22:15.247 回答
10

要回答我自己的问题:

在 iOS8 自定义键盘中,extensionContext 对象为 nil,因此我无法使用它来启动包含的应用程序。

我想出的解决方法是:

  1. 为您的应用创建一个 url 方案
  2. 将 UIWebView 添加到您的 inputView
  3. 在 webview 中加载包含应用程序的 url 方案

我不确定 Apple 是否会允许这种情况发生,但现在可以了。

于 2014-07-06T13:18:16.117 回答
10

这是键盘扩展的工作解决方案(在 iOS 9.2 上测试)。该类别添加了访问隐藏sharedApplication对象然后调用openURL:它的特殊方法。(当然,你必须在openURL:你的应用方案中使用方法。)

// Usage:
// UIInputViewController.openURL(NSURL(string: "your-app-scheme://")!)
extension UIInputViewController {

    func openURL(url: NSURL) -> Bool {
        do {
            let application = try self.sharedApplication()
            return application.performSelector("openURL:", withObject: url) != nil
        }
        catch {
            return false
        }
    }

    func sharedApplication() throws -> UIApplication {
        var responder: UIResponder? = self
        while responder != nil {
            if let application = responder as? UIApplication {
                return application
            }

            responder = responder?.nextResponder()
        }

        throw NSError(domain: "UIInputViewController+sharedApplication.swift", code: 1, userInfo: nil)
    }

}

最近我开发了稍微不同的方法:

// Usage:
// UIApplication.sharedApplication().openURL(NSURL(string: "your-app-scheme://")!)

extension UIApplication {

    public static func sharedApplication() -> UIApplication {
        guard UIApplication.respondsToSelector("sharedApplication") else {
            fatalError("UIApplication.sharedKeyboardApplication(): `UIApplication` does not respond to selector `sharedApplication`.")
        }

        guard let unmanagedSharedApplication = UIApplication.performSelector("sharedApplication") else {
            fatalError("UIApplication.sharedKeyboardApplication(): `UIApplication.sharedApplication()` returned `nil`.")
        }

        guard let sharedApplication = unmanagedSharedApplication.takeUnretainedValue() as? UIApplication else {
            fatalError("UIApplication.sharedKeyboardApplication(): `UIApplication.sharedApplication()` returned not `UIApplication` instance.")
        }

        return sharedApplication
    }

    public func openURL(url: NSURL) -> Bool {
        return self.performSelector("openURL:", withObject: url) != nil
    }

}
于 2015-12-23T00:33:01.270 回答
5

Apple 不允许除 Today 扩展之外的任何应用扩展来打开包含的应用。

从指南:

Today 小部件(并且没有其他应用程序扩展类型)可以通过调用 NSExtensionContext 类的 openURL:completionHandler: 方法来请求系统打开其包含的应用程序。

你可以在这里查看

于 2015-02-04T06:20:20.700 回答
2

根据 Apple 的文档https://developer.apple.com/library/ios/documentation/General/Conceptual/ExtensibilityPG/index.html,您应该像这样使用 NSExtensionContext :

NSExtensionContext *ctx = [[NSExtensionContext alloc] init];
[ctx openURL:[NSURL URLWithString:@"myapp://"] completionHandler:^(BOOL success){return ;}];
于 2014-12-08T13:22:33.093 回答
2

我尝试为最新的 xcode 8.2 和 swift 3.0 做上述解决方案。

很遗憾。我无法让它工作。所以我找到了自己的解决方案,它在 swift 3.0、xcode 8.2 中运行良好

   func openURL(_ url: URL) {
        return
   }

   func openApp(_ urlstring:String) {

       var responder: UIResponder? = self as UIResponder
       let selector = #selector(openURL(_:))
       while responder != nil {
           if responder!.responds(to: selector) && responder != self {
              responder!.perform(selector, with: URL(string: urlstring)!)
              return
             }
             responder = responder?.next
          }
   }

  // Usage
  //call the method like below
  //self.openApp(urlString)

  //URL string need to included custom scheme.
  //for example, if you created scheme name = customApp
  //urlString will be "customApp://?[name]=[value]"
  //self.openApp("customApp://?category=1")
于 2017-01-25T03:46:52.073 回答
1

这是我发现使用上述内容打开任何 URL 的内容:

UIWebView * webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
NSString *urlString = @"https://itunes.apple.com/us/app/watuu/id304697459";
NSString * content = [NSString stringWithFormat : @"<head><meta http-equiv='refresh' content='0; URL=%@'></head>", urlString];
[webView loadHTMLString:content baseURL:nil];
[self.view addSubview:webView];
[webView performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:2.0];

请注意,在这种情况下,我将从 UIInputViewController 实例化此调用。

此方法也应该使用包含应用程序的 URL 方案

注意:从 iOS 8.3 开始,Apple 已经取消了这种方法

于 2015-01-20T03:27:20.647 回答
1

在应用程序扩展(例如:自定义键盘)中,这本来可以通过 iOS 8.1.2 处理,UIViewController.extensionContext但从 iOS 8.1.2 开始,该字段nil位于以下 Swift 调用中:

self.extensionContext?.openURL(appURL, completionHandler: nil)
// Does nothing because extensionContext is nil (iOS 8.1)

实际上,也不可能像Apple 文档Application.sharedApplication.openURL(...)中所述的那样在 App Extension 中使用。

因此,从 8.1.2 开始,解决方法是使用哑UIWebView元重定向到包含应用程序,如下所示:

let webView = UIWebView(frame: CGRectMake(0, 0, 0, 0));
let url = "MyKeyboard://";
let content = "<head><meta http-equiv='refresh' content='0; URL=\(url)'></head>";
webView.loadHTMLString(content, baseURL: nil);
self.view.addSubview(webView);
let delayInSeconds = 2.0
let startTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_SEC)))
dispatch_after(startTime, dispatch_get_main_queue()) { () -> Void in
    webView.removeFromSuperview()
}

WhereMyKeyboard://必须相应地定义为包含应用程序中的 URL 方案。

请注意,我还不知道这是否得到 Apple 的批准。从自定义键盘转发到应用程序可能对用户体验来说已经够糟糕了。

于 2015-01-25T20:41:02.477 回答
1

一个 Swift 2.2 版本,类似于 DongHyun Jang 对 Objective-C 的回答:

func webLink(urlString: String) {
    let url = NSURL(string: urlString)
    var responder: UIResponder? = self
    while let r = responder {
        if r.respondsToSelector("openURL:") {
            r.performSelector("openURL:", withObject: url)
            break;
        }
        responder = r.nextResponder()
    }  
}
于 2016-06-22T19:35:40.863 回答
1

根据 Apple 的扩展审查指南,现在建议从扩展中打开外部应用程序。

如需参考,请参阅苹果审查指南

第 4.4.1 节

他们不得:

  • 包括营销、广告或应用内购买;
  • 启动除设置之外的其他应用程序;或者
于 2017-06-20T01:45:36.073 回答
0

我为此挣扎了几天。我无法让 UIWebView 在自定义键盘内工作。

修复:将它放在 viewDidAppear:animated 中而不是 viewDidLoad 中(另一方面,这也是键盘自定义高度的代码应该保留的地方)。

代码:

- (void)viewDidAppear:(BOOL)animated {
    UIWebView * webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    NSString * content = @"<a href='your_app_schema://your_params'>anchor</a>";
    [webView loadHTMLString:content baseURL:nil];
    [self.view addSubview:webView];
}
于 2014-09-12T22:07:21.477 回答