19

I am using UIDocumentInteractionController for showing popover menu "Open In..." so that user can open a document in other application.

Method presentOpenInMenuFromBarButtonItem:animated: returns NO in case there is no application able to open given document (menu will not show). But it is too late for me to wait until getting so far. I would like to disable the button initiating that opening if it is not possible instead of raising expectations of an user and then say "sorry, it is not possible to open it".

Is it possible to query system to see if there is at least one application registered for particular document type? I have checked canPreviewItem: in QLPreviewController, but it seems it doesn't support the same document types which UIDocumentInteractionController can handle.

palob
  • 372
  • 1
  • 3
  • 11

5 Answers5

11

[EDIT] Not working for iOS 6.0 (see comment)

It seems that dismissMenuAnimated (with no animation at all) is the key:

-(BOOL)canOpenDocumentWithURL:(NSURL*)url inView:(UIView*)view {
    BOOL canOpen = NO;
    UIDocumentInteractionController* docController = [UIDocumentInteractionController 
                                                   interactionControllerWithURL:url];
    if (docController)
    {
        docController.delegate = self;
        canOpen = [docController presentOpenInMenuFromRect:CGRectZero
                                   inView:self.view animated:NO];                   
        [docController dismissMenuAnimated:NO];
    }
    return canOpen;
}

It will return YES if at least one application is able to open the file pointed by url. At least it's working in my case (KMZ files), testing with/without Dropbox app on iPhone iOS 4.3.
Actually, it seems to work even if url is not pointing to an actual file (i.e. @"test.kmz"), but I wouldn't rely on it for all file types.

FKDev
  • 2,266
  • 1
  • 20
  • 23
  • This was the solution I adopted previously, but a word of warning for iOS 6. It seems that presenting and dismissing the controller in this way causes some side effects to UITabBar - specifically, the 'UITabBarButton's (private API) that make up the tab bar are hidden but not unhidden. From a bit of digging it seems to be that the buttons are set to 0 alpha, then hidden in an animation completion block on calling the 'present' method. Unfortunately the animation completion block is executed after the 'dismiss' method is called so the buttons remain hidden. – Weaverfish Sep 21 '12 at 11:16
  • Any solution to this problem..? – Paresh Thakor Nov 02 '12 at 07:03
  • 1
    I got the same issue as @Weaverfish, so what I did was to present in the window instead of the view. `[docController presentOptionsMenuFromRect:window.bounds inView:window animated:NO];` – XCool Dec 29 '12 at 16:17
  • 1
    `UIView *v = [[UIView alloc] init];` `canOpen = [docController presentOpenInMenuFromRect:CGRectZero inView:v animated:NO];` – Everton Cunha Jan 10 '14 at 16:41
  • This looks like a neat trick. Unfortunately, if you have DropBox or OneDrive installed they claim to open anything. You can also now add anything to Notes. – Bob Wakefield Jun 21 '16 at 16:35
5

I came up with a less hacky way of doing things, but there is a limitation that you can only detect whether there's a compatible app after the user has selected to open in an app. This will enable you to provide the same user experience as the Dropbox app.

All you need to do is set up the UIDocumentInteractionControllerDelegate and create a boolean flag property that holds whether or not the menu was presented.

In the interface:

/**
 The document interaction controller used to present the 'Open with' dialogue.
 */
@property (nonatomic,strong) UIDocumentInteractionController *documentInteractionController;

/**
 Boolen that holds whether or not there are apps installed that can open the URL.
 */
@property (nonatomic) BOOL hasCompatibleApps;

In the implementation:

- (void)shareFileAtURL:(NSURL*)fileURL
{
    [self setDocumentInteractionController:[UIDocumentInteractionController interactionControllerWithURL:fileURL]];
    [[self documentInteractionController] setDelegate:self];

    [self setHasCompatibleApps:NO];

    [[self documentInteractionController] presentOpenInMenuFromRect:[self popoverRect] inView:[self popoverView] animated:YES];

    if (![self hasCompatibleApps])
    {
        // Show an error message to the user.
    }
}

#pragma mark - UIDocumentInteractionControllerDelegate methods

- (void)documentInteractionControllerWillPresentOpenInMenu:(UIDocumentInteractionController *)controller
{
    [self setHasCompatibleApps:YES];
}

I hope that helps some people.

Luke Rogers
  • 2,369
  • 21
  • 28
3

This works for me:

   self.docController = [UIDocumentInteractionController interactionControllerWithURL:url];
   UIView *v = [[UIView alloc] init];
   BOOL isAnAppAvalaible = [self.docController presentOpenInMenuFromRect:CGRectZero inView:v animated:NO];
Kaptain
  • 1,358
  • 12
  • 20
1
NSURL *url = [NSURL URLWithString:@"path_to_the_file"];
UIDocumentInteractionController *controller =
    [UIDocumentInteractionController interactionControllerWithURL:url];
BOOL openResult = [controller presentPreviewAnimated:NO];

If you use presentPreviewAnimated: for showing files you can use openResult to detect if it was opened successfully.

Rostyslav Druzhchenko
  • 3,673
  • 3
  • 33
  • 38
  • presentPreviewAnimated: always returns NO for me. I will stick with presentOpenInMenuFromRect:inView:animated: where it is presented in view moved off-screen. – palob May 06 '15 at 13:31
-4

-[UIApplication canOpenURL:] should do the job.

Yuji
  • 34,103
  • 3
  • 70
  • 88
  • 2
    It seems canOpenURL resolves only schemes of URL. In this case I have always file:// and I would like to check different document types (pdf, jpeg, ...) – palob Feb 10 '11 at 15:51
  • In any case, doesn't canOpenURL: determine if your application can open the document, not if OTHER applications you have installed can do so? – Roger Mar 09 '11 at 09:21
  • Uh, we know what files our program we're writing can open, right? Because it's us who are writing the app? `canOpenURL:` determines if there are OTHER apps which can open the scheme, see the comment in the official documentation linked above. On OS X this functionality is in `NSWorkspace` and it is separate from `NSApplication`. But somehow Apple decided to combine them on iOS. Strange. – Yuji Mar 09 '11 at 10:28