在Action Extension中openURL不起作用
我添加下面的代码:
- (IBAction)done { // Return any edited content to the host app. // This template doesn't do anything, so we just echo the passed in items. NSURL *url = [NSURL URLWithString:@"lister://today"]; [self.extensionContext openURL:url completionHandler:^(BOOL success) { NSLog(@"fun=%s after completion. success=%d", __func__, success); }]; [self.extensionContext completeRequestReturningItems:self.extensionContext.inputItems completionHandler:nil]; }
在创buildAction Extension目标之后。 但它不能工作。
我的目的是:当用户在Photos.app(iOS的默认Photos.app或被称为图库)中查看照片,然后单击共享button启动我们的扩展视图。 我们可以将Photos.app中的图像传输到我自己的应用程序,并在我的应用程序中处理或上传图像。
我也尝试“CFBundleDocumentTypes”,但它也不能工作。
任何帮助将不胜感激。
这是devise。 我们不希望自定义操作成为应用程序启动器。
这是我以前做的事情:
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实例化这个调用。
此方法也应该使用来自包含应用程序的URLscheme
更新04/17/2015:这不适用于iOS 8.3。 我们正在寻找解决scheme,我们将尽快更新答案
更新06/01/2015:我们find了一个适用于iOS 8.3的解决scheme
var responder = self as UIResponder? while (responder != nil){ if responder!.respondsToSelector(Selector("openURL:")) == true{ responder!.callSelector(Selector("openURL:"), object: url, delay: 0) } responder = responder!.nextResponder() }
这将find一个合适的响应者发送openURL到。
你需要添加这个扩展,replaceswift的performSelector,并帮助构build机制:
extension NSObject { func callSelector(selector: Selector, object: AnyObject?, delay: NSTimeInterval) { let delay = delay * Double(NSEC_PER_SEC) let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay)) dispatch_after(time, dispatch_get_main_queue(), { NSThread.detachNewThreadSelector(selector, toTarget:self, withObject: object) }) } }
更新2015年6月15日:Objective-C
有人问Objective-C中的代码,所以在这里。 我不会去运行它,因为我现在没有时间,但它应该是非常简单的:
UIResponder *responder = self; while(responder){ if ([responder respondsToSelector: @selector(OpenURL:)]){ [responder performSelector: @selector(OpenURL:) withObject: [NSURL URLWithString:@"www.google.com" ]]; } responder = [responder nextResponder]; }
如上所述,我没有运行这个Objective-C代码,它只是从Swift代码转换而来。 请让我知道如果您遇到任何问题和解决scheme,我会更新它。 现在,我只是使用迅速,不幸的是我的大脑正在贬低Objective-C
更新05/02/2016:弃用function
正如@KyleKIM指出的那样,Selector函数已经被#selector在Swift 2.2中replace了。 此外,还有一个弃用的function,可能会在Swift 3.0中被删除,所以我正在做一些研究来寻找替代scheme。
更新09/16/2016:XCode 8,Swift 3.0和iOS10下面的代码仍然在上述版本上工作。 你会得到一些警告:
let url = NSURL(string:urlString) let context = NSExtensionContext() context.open(url! as URL, completionHandler: nil) var responder = self as UIResponder? while (responder != nil){ if responder?.responds(to: Selector("openURL:")) == true{ responder?.perform(Selector("openURL:"), with: url) } responder = responder!.next }
更新6/15/2017:XCode 8.3.3
let url = NSURL(string: urlString) let selectorOpenURL = sel_registerName("openURL:") let context = NSExtensionContext() context.open(url! as URL, completionHandler: nil) var responder = self as UIResponder? while (responder != nil){ if responder?.responds(to: selectorOpenURL) == true{ responder?.perform(selectorOpenURL, with: url) } responder = responder!.next }
试试这个代码。
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]]; } }
苹果公司接受了以下解决scheme,这是主机应用程序使用的“相同”代码。 它适用于所有iOS 8版本(在iOS 8.0 – iOS 8.3上testing)。
NSURL *destinationURL = [NSURL URLWithString:@"myapp://"]; // Get "UIApplication" class name through ASCII Character codes. NSString *className = [[NSString alloc] initWithData:[NSData dataWithBytes:(unsigned char []){0x55, 0x49, 0x41, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E} length:13] encoding:NSASCIIStringEncoding]; if (NSClassFromString(className)) { id object = [NSClassFromString(className) performSelector:@selector(sharedApplication)]; [object performSelector:@selector(openURL:) withObject:destinationURL]; }
在Swift 3.0中工作的解决scheme:
// For skip compile error. func openURL(_ url: URL) { return } func openContainerApp() { 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: "containerapp://")!) return } responder = responder?.next } }
说明:
在扩展中,api被编译器限制为不允许您像容器应用程序中那样使用openURl(:URL)。 但是API仍然在这里。
我们不能在我们的类中执行方法,直到我们声明它,我们真正想要的是让UIApplication执行这个方法。
回想一下响应链,我们可以使用
var responder: UIResponder? = self as UIResponder responder = responder?.next
循环到UIApplication对象。
我的这个方法的应用程序通过审查过程,所以不用担心使用它。
这似乎是一个错误,因为文档说:
打开包含的应用程序
在某些情况下,扩展请求打开包含的应用程序是有意义的。 例如,当用户单击某个事件时,OS X中的“日历”小部件将打开“日历”。 为确保您的包含应用以用户当前任务的上下文有意义的方式打开,您需要定义应用及其扩展可以使用的自定义URLscheme。
扩展程序不会直接告诉其包含的应用程序打开; 相反,它使用NSExtensionContext的openURL:completionHandler:方法来告诉系统打开其包含的应用程序。 当一个扩展使用这个方法打开一个URL时,系统在完成之前validation这个请求。
我今天报告了它: http : //openradar.appspot.com/17376354如果你有空闲时间,你应该弄懂它。
键盘扩展的工作解决scheme(在iOS 9.2上testing) 。 这个类别增加了访问隐藏的sharedApplication
对象的特殊方法,然后调用openURL:
(当然,你必须在你的应用程序scheme中使用openURL:
方法。)
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) } }
一个可能的解决方法:创build一个小的UIWebView并添加到您的视图中,并使用上面设置的urlscheme运行它的方法loadRequest。 这是一种解决方法,我不确定苹果会对此说些什么。 祝你好运!
NSExtensionContext在今天的扩展中只支持openURL函数,这是在苹果的文档中介绍的关于NSExtensionContext的。原文是“每个扩展点决定是否支持这个方法,或者在哪个条件下支持这个方法。点支持这种方法。“
Julio Bailon的回答与现代Swift语法的更新版本:
let url = NSURL(string: "scheme://")! var responder: UIResponder? = self while let r = responder { if r.respondsToSelector("openURL:") { r.performSelector("openURL:", withObject: url) break } responder = r.nextResponder() }
现在不需要扩展NSObject。
注意:在调用此代码之前,您必须等待视图附加到视图层次结构,否则不能使用响应者链。
以下代码适用于Xcode 8.3.3,iOS10,Swift3和Xcode 9,iOS11,Swift4,没有任何编译器警告:
func openUrl(url: URL?) { let selector = sel_registerName("openURL:") var responder = self as UIResponder? while let r = responder, !r.responds(to: selector) { responder = r.next } _ = responder?.perform(selector, with: url) } func canOpenUrl(url: URL?) -> Bool { let selector = sel_registerName("canOpenURL:") var responder = self as UIResponder? while let r = responder, !r.responds(to: selector) { responder = r.next } return (responder!.perform(selector, with: url) != nil) }
解决scheme为最新的iOS SDK 10.2。 以前的所有解决scheme都使用已弃用的API。 此解决scheme基于search托pipe应用程序的UIApplication UIResponder(此应用程序为我们的扩展创build执行上下文)。 该解决scheme只能在Objective-C中提供,因为有一个3参数的方法来调用,这是不可能与performSelector:
方法。 要调用这个不被弃用的方法openURL:options:completionHandler:
我们需要使用Swift中不可用的NSInvocation实例。 提供的解决scheme可以从Objective-C和Swift(任何版本)中调用。 我需要说,我不知道如果提供的解决scheme将有效的苹果审查过程。
UIViewController+OpenURL.h
#import <UIKit/UIKit.h> @interface UIViewController (OpenURL) - (void)openURL:(nonnull NSURL *)url; @end
UIViewController+OpenURL.m
#import "UIViewController+OpenURL.h" @implementation UIViewController (OpenURL) - (void)openURL:(nonnull NSURL *)url { SEL selector = NSSelectorFromString(@"openURL:options:completionHandler:"); UIResponder* responder = self; while ((responder = [responder nextResponder]) != nil) { NSLog(@"responder = %@", responder); if([responder respondsToSelector:selector] == true) { NSMethodSignature *methodSignature = [responder methodSignatureForSelector:selector]; NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature]; // Arguments NSDictionary<NSString *, id> *options = [NSDictionary dictionary]; void (^completion)(BOOL success) = ^void(BOOL success) { NSLog(@"Completions block: %i", success); }; [invocation setTarget: responder]; [invocation setSelector: selector]; [invocation setArgument: &url atIndex: 2]; [invocation setArgument: &options atIndex:3]; [invocation setArgument: &completion atIndex: 4]; [invocation invoke]; break; } } } @end
从Swift 3开始只有当您的视图控制器处于视图层次结构中时才可以执行此操作。 这是我如何使用它的代码:
override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let context = self.extensionContext! let userAuthenticated = self.isUserAuthenticated() if !userAuthenticated { let alert = UIAlertController(title: "Error", message: "User not logged in", preferredStyle: .alert) let cancel = UIAlertAction(title: "Cancel", style: .cancel) { _ in context.completeRequest(returningItems: nil, completionHandler: nil) } let login = UIAlertAction(title: "Log In", style: .default, handler: { _ in //self.openContainingAppForAuthorization() let url = URL(string: "fashionapp://login")! self.open(url) context.completeRequest(returningItems: nil, completionHandler: nil) }) alert.addAction(cancel) alert.addAction(login) present(alert, animated: true, completion: nil) } }
我的猜测是,这是故意不可能的。 openURL:completionHandler:
块表示可能不支持所有扩展types,动作扩展文档显式地说:
如果您想帮助用户在社交网站上分享内容,或向用户提供关于他们所关心的信息的最新信息,则“行动”扩展点不是正确的select。
我认为共享扩展可能更合适,但这两种types的文档都build议将体验embedded到主机应用程序中,而不是将用户带到应用程序中,因此可能也不允许这样做。 因此,也许按照共享扩展文档,只是从扩展的用户界面内上传你的图像,因为它表明?
不是每个应用程序扩展types都支持“extensionContext openURL”。
我testing了iOS 8 beta 4,发现今天扩展支持它,但键盘扩展没有。
只有今天扩展似乎工作。
这不是100%的logging,但苹果员工明确表示,键盘扩展不支持openURL:completionHandler。
该文件说:
每个扩展点决定是否支持这个方法,或者在哪个条件下支持这个方法。
因此,在实践中,共享,操作,键盘和文档提供程序不适用于任何人(testing版5),只有今日扩展支持。
作为苹果文档“A Today小部件(并且没有其他应用程序扩展types)可以通过调用NSExtensionContext类的openURL:completionHandler:方法来请求系统打开其包含的应用程序。
对于其他扩展,我使用这个解决scheme
UIWebView * webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)]; NSString *urlString = @"ownApp://"; // Use other application url schema. 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:1.0];