ios openURL 在操作扩展中不起作用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24297273/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
openURL not work in Action Extension
提问by Laurence Fan
I add following code:
我添加以下代码:
- (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];
}
after I create the Action Extension target. But it can not work.
在我创建 Action Extension 目标之后。但它不能工作。
My purpose is that: when user view a photo in Photos.app (the iOS's default Photos.app or called gallery), and he click the share button to launch our extension view. We can transfer the image from Photos.app to my own app and deal or upload the image in my app.
我的目的是:当用户在 Photos.app(iOS 的默认 Photos.app 或称为画廊)中查看照片时,他单击共享按钮以启动我们的扩展视图。我们可以将图片从 Photos.app 传输到我自己的应用程序,并在我的应用程序中处理或上传图像。
I also try "CFBundleDocumentTypes" but it also can not work.
我也尝试了“CFBundleDocumentTypes”,但它也无法工作。
Any help will be appreciated.
任何帮助将不胜感激。
采纳答案by Ian Baird
This is by design. We don't want Custom Actions to become app launchers.
这是设计使然。我们不希望自定义操作成为应用启动器。
回答by Julio Bailon
This is what I used to do:
这是我以前做的:
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];
Please note that in this case I am instantiating this call from the UIInputViewController.
请注意,在这种情况下,我从 UIInputViewController 实例化此调用。
This method should also work using the URL scheme from the containing app
此方法也应该使用包含应用程序的 URL 方案
UPDATE 04/17/2015: This does not work with iOS 8.3. We are looking for a solution and we will update the answer soon
2015 年 4 月 17 日更新:这不适用于 iOS 8.3。我们正在寻找解决方案,我们会尽快更新答案
UPDATE 06/01/2015: We found a solution that works in iOS 8.3
更新 06/01/2015:我们找到了一个适用于 iOS 8.3 的解决方案
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()
}
This will find a suitable responder to send the openURL to.
这将找到一个合适的响应者来发送 openURL。
You need to add this extension that replaces the performSelector for swift and helps in the construction of the mechanism:
您需要添加此扩展来替换 swift 的 performSelector 并帮助构建机制:
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)
})
}
}
UPDATE 06/15/2015: Objective-C
2015 年 6 月 15 日更新:Objective-C
Someone asked for the code in Objective-C so here it is. I am not going to run it as I don't have the time right now but it should be quite straightforward:
有人要求使用 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];
}
As mentioned, I have not run this Objective-C code, it is just a conversion from the Swift code. Please let me know if you encounter any issues and the solution and I will update it. Nowadays, I am just using swift and unfortunately my brain is deprecating Objective-C
如前所述,我没有运行这个 Objective-C 代码,它只是 Swift 代码的转换。如果您遇到任何问题和解决方案,请告诉我,我会更新它。现在,我只是在使用 swift,不幸的是我的大脑正在弃用 Objective-C
UPDATE 05/02/2016: Deprecated functions
2016 年 5 月 2 日更新:已弃用的功能
As pointed by @KyleKIM the Selector functions have been replaced in Swift 2.2 by #selector. Also, there is a function that is deprecated and will probably get removed in Swift 3.0 so I am doing some research to find an alternative.
正如@KyleKIM 所指出的,Swift 2.2 中的 Selector 函数已被 #selector 替换。此外,有一个函数已被弃用,可能会在 Swift 3.0 中被删除,所以我正在做一些研究以寻找替代方案。
UPDATE 09/16/2016: XCode 8, Swift 3.0 and iOS10The following code is still working on the mentioned versions. You will get some warnings:
2016 年 9 月 16 日更新: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
}
UPDATE 6/15/2017: XCode 8.3.3
2017 年 6 月 15 日更新: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
}
回答by DongHyun Jang
Try this code.
试试这个代码。
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]];
}
}
回答by Hank Brekke
Apple accepted the following solution, which is the "same" code that a host app would use. It works on all iOS 8 versions to date (tested on iOS 8.0 - iOS 8.3).
Apple 接受了以下解决方案,这是主机应用程序将使用的“相同”代码。它适用于迄今为止的所有 iOS 8 版本(在 iOS 8.0 - iOS 8.3 上测试)。
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];
}
回答by Alen Liang
Worked solution in Swift 3.0 & 4.0:
在 Swift 3.0 和 4.0 中工作的解决方案:
// 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
}
}
Explanation:
解释:
In extension, api is limited by compiler to not let you use openURl(:URL) like in container app. However the api is still here.
在扩展中,api 受编译器限制,不允许您像在容器应用程序中那样使用 openURl(:URL)。但是api仍然在这里。
And we can't perform method in our class until we declare it, what we really want is let UIApplication to perform this method.
并且我们不能在我们的类中执行方法,直到我们声明它,我们真正想要的是让 UIApplication 执行这个方法。
Recall to responder chain, we can use
回想响应者链,我们可以使用
var responder: UIResponder? = self as UIResponder
responder = responder?.next
to loop to UIApplication object.
循环到 UIApplication 对象。
And my apps with this method pass the review process, so don't worry to use it.
而且我的应用程序使用这种方法通过了审核过程,所以不用担心使用它。
回答by Valentin Shergin
Working solution (tested on iOS 9.2)for Keyboard Extension. This category adds special method for access to hidden sharedApplication
object and then call openURL:
on it.
(Of course then you have to use openURL:
method with your app scheme.)
键盘扩展的工作解决方案(在 iOS 9.2 上测试)。该类添加了访问隐藏sharedApplication
对象的特殊方法,然后调用openURL:
它。(当然,你必须在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)
}
}
回答by fiona zhou
NSExtensionContext only support openURL function in today extension ,this is described in apple's documents about NSExtensionContext.The original words is "Each extension point determines whether to support this method, or under which conditions to support this method. In iOS 8.0, only the Today extension point supports this method."
NSExtensionContext在today扩展中只支持openURL功能,这在苹果关于NSExtensionContext的文档中有描述。原文是“每个扩展点决定是否支持这个方法,或者在什么条件下支持这个方法。在iOS 8.0中,只有Today扩展点支持这种方法。”
回答by Arek Holko
It seems to be a bug, because docs say:
这似乎是一个错误,因为文档说:
Opening the Containing App
In some cases, it can make sense for an extension to request its containing app to open. For example, the Calendar widget in OS X opens Calendar when users click an event. To ensure that your containing app opens in a way that makes sense in the context of the user's current task, you need to define a custom URL scheme that both the app and its extensions can use.
An extension doesn't directly tell its containing app to open; instead, it uses the openURL:completionHandler: method of NSExtensionContext to tell the system to open its containing app. When an extension uses this method to open a URL, the system validates the request before fulfilling it.
打开包含的应用程序
在某些情况下,扩展请求打开其包含的应用程序是有意义的。例如,当用户单击事件时,OS X 中的日历小部件会打开日历。为了确保您的包含应用程序以在用户当前任务的上下文中有意义的方式打开,您需要定义应用程序及其扩展程序都可以使用的自定义 URL 方案。
扩展程序不会直接告诉其包含的应用程序打开;相反,它使用 NSExtensionContext 的 openURL:completionHandler: 方法告诉系统打开其包含的应用程序。当扩展程序使用此方法打开 URL 时,系统会在完成请求之前验证请求。
I reported it today: http://openradar.appspot.com/17376354You should dupe it, if you have some free time.
我今天报告了它:http: //openradar.appspot.com/17376354如果你有空闲时间,你应该欺骗它。
回答by detman
A possible workaround: Create and add a small UIWebView to your view and run it's method loadRequest with the url scheme you set above. This is a workaround and I'm not sure what Apple will say about it. Good luck!
一种可能的解决方法:创建一个小的 UIWebView 并将其添加到您的视图中,然后使用您在上面设置的 url 方案运行它的方法 loadRequest。这是一种解决方法,我不确定 Apple 会怎么说。祝你好运!
回答by Pulsar
An updated version of Julio Bailon's answer with modern Swift syntax:
使用现代 Swift 语法的 Julio Bailon 答案的更新版本:
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()
}
There is no need for an extension for NSObject now.
现在不需要 NSObject 的扩展。
Note: you must wait for the view to be attached to the view hierarchy before calling this code otherwise the responder chain can't be used.
注意:在调用此代码之前,您必须等待视图附加到视图层次结构,否则无法使用响应者链。