7

如何知道应用程序支持哪个 UTI 将文件发送到不同的应用程序?假设该文件没有文件扩展名,但我碰巧知道该文件的 UTI。

我尝试了以下方法:

// target is a NSURL with the location of the extension less file on the system
// knownUTI is a NSString containing the UTI of the file 
    UIDocumentInteractionController* dic = [UIDocumentInteractionController interactionControllerWithURL:target];  
    [dic retain];

    dic.delegate = self;
    dic.UTI = knownUTI; 
    [dic presentOpenInMenuFromRect:CGRectZero inView:superController.view animated:YES]

它显示支持的应用程序,但是,如果我选择它,它不会切换应用程序。代表打电话给

- (void)documentInteractionController:(UIDocumentInteractionController *)controller willBeginSendingToApplication:(NSString *)application

- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application

永远不会被调用,并且应用程序永远不会切换。

目标 App 以如下方式导出其 UTI:

    <key>CFBundleDocumentTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeIconFiles</key>
            <array/>
            <key>CFBundleTypeName</key>
            <string>Migration DocType</string>
            <key>CFBundleTypeRol</key>
            <string>Shell</string>
            <key>LSHandlerRank</key>
            <string>Owner</string>
            <key>LSItemContentTypes</key>
            <array>
                <string>com.mycomp.customstring</string>
            </array>
        </dict>
    </array>

...

<key>UTExportedTypeDeclarations</key>
    <array>
        <dict>
            <key>UTTypeConformsTo</key>
            <array>
                <string>public.data</string>
            </array>
            <key>UTTypeDescription</key>
            <string>My custom UTI</string>
            <key>UTTypeIdentifier</key>
            <string>com.mycomp.customstring</string>
        </dict>
    </array>

由于这不起作用,我还尝试添加自定义扩展。不过,它不会以这种方式工作。将自定义扩展名添加到我移交给的文件时DocumentInteractionController,它可以工作。但是,应用程序列表显示了支持相同文件扩展名的所有其他应用程序,无论 I 提供的 UTI 类型如何。

假设我在 2 个不同的应用程序中声明了 2 个 UTI:

App1 with UTI1: com.mycomp.a  with extension .abc
App2 with UTI2: com.mycomp.b  with extension .abc

当将文件交给 DocumentInteractionController 并将 UTI 设置为com.mycomp.a它时,也会将 App2 显示为能够处理文件的可能应用程序。

我以以下方式定义了一个带有扩展名的UTI:

<key>UTExportedTypeDeclarations</key>
    <array>
        <dict>
            <key>UTTypeConformsTo</key>
            <array>
                <string>public.data</string>
            </array>
            <key>UTTypeDescription</key>
            <string>My UTI Type</string>
            <key>UTTypeIdentifier</key>
            <string>com.mycomp.a</string>
            <key>UTTypeTagSpecification</key>
            <dict>
                <key>public.filename-extension</key>
                <string>abc</string>
                <key>public.mime-type</key>
                <string>application/abc</string>
            </dict>
        </dict>
    </array>

我真的很感谢你的帮助,我有点卡住了。所以,又是一个问题:如何将文件发送到具有已知 UTI 的应用程序,或者没有扩展名,或者与我不想在 DocumentInteractionController 中将应用程序显示为选项的其他文件具有相同的扩展名?

谢谢

4

1 回答 1

2

我找到了解决这个问题的方法。但是,我认为这不是一个很好的。

在测试期间,我发现当离开文件扩展名时,UIDocumentInteractionController将根据UTI我指定的显示应用程序。将文件发送到目标应用程序时不会发生任何事情。我得出结论,我需要一个文件扩展名来进行最终发送。

URL我的方法是在文件发送到目标应用程序之前修改属性,并为其提供相同的文件,但文件扩展名是目标应用程序接受的。然而,我的应用程序刚刚崩溃。我用 Instruments 对其进行了分析,发现问题是由于UIDocumentInteractionController过度释放了一些代理对象。我还看到最终的过度释放是在一个名为_invalidateof UIDocumentInteractionController(Private)category 的方法中调用的,它释放了对象。

由于类别不能被其他类别覆盖,我决定用我自己的实现来调整类别方法,检查 URL 是否包含文件扩展名,并将调用重定向到原始_invalidate方法或什么都不做。

以下代码显示了我所做的:

#include <objc/runtime.h>

@interface UIDocumentInteractionController(InvalidationRedirect)

-(void)_invalidateMY;
+(void)load;
void Swizzle(Class c, SEL orig, SEL newSEL);
@end

@implementation UIDocumentInteractionController(InvalidationRedirect)

void Swizzle(Class c, SEL orig, SEL newSEL)
{
    Method origMethod = class_getInstanceMethod(c, orig);
    Method newMethod = class_getInstanceMethod(c, newSEL);
    if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
        class_replaceMethod(c, newSEL, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
    else
        method_exchangeImplementations(origMethod, newMethod);
}

-(void)_invalidateMY{
    @synchronized(self) {
        if(![[[[self.URL lastPathComponent] componentsSeparatedByString:@"."] lastObject] isEqualToString:@"extension"]) {
            [self _invalidateMY];
        }
    }
}

+(void)load
{
    Swizzle([UIDocumentInteractionController class], @selector(_invalidate), @selector(_invalidateMY));
}

@end

此代码将原始_invalidate方法与交换_invalidateMY,导致每次调用_invalidate调用_invalidateMY,反之亦然。

以下代码显示了我如何处理UIDocumentInteractionController

  // create a file without extension
  NSString *fileName = @"myFile";

  NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

  NSString *documentsDirectory = [paths objectAtIndex:0];

  NSURL* target = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@", documentsDirectory, fileName]];

  if([[@"THIS IS JUST A TEST STRING" dataUsingEncoding:NSUTF8StringEncoding] writeToURL:target atomically:NO]) {
      NSLog(@"file written successfully");
  }else {
      NSLog(@"Error.. file writing failed");
  }

  UIDocumentInteractionController* dic = [UIDocumentInteractionController interactionControllerWithURL:target];
  [dic retain];
  dic.delegate = self;


  // set the UTI to the known UTI we want to list applications for
  dic.UTI = @"com.mycomp.a";

  [dic presentOpenInMenuFromRect:CGRectZero inView:superController.view animated:YES];

这段代码显示了UIDocumentInteractionController交换 URL 的委托方法:

- (void)documentInteractionController:(UIDocumentInteractionController *)controller willBeginSendingToApplication:(NSString *)application
{
    NSFileManager *fileMgr = [NSFileManager defaultManager];
    NSError *error;
    NSURL* newTarget = [NSURL URLWithString:[NSString stringWithFormat:@"%@.extension", controller.URL]];
    // rename file to file with extension
    if (![fileMgr moveItemAtURL:controller.URL toURL:newTarget error:&error] && error) {
        NSLog(@"Error moving file: %@", [error localizedDescription]);
    }
    @synchronized(controller) {
        //exchange URL with URL+extension
        controller.URL = newTarget; //<- this results in calling _invalidate
    }
    NSLog(@"%@", [NSString stringWithContentsOfURL:controller.URL encoding:NSUTF8StringEncoding error:nil]);
}

这个解决方案有效,但在我看来这是一个肮脏的黑客,必须有一个更好的解决方案。

于 2011-12-13T17:16:08.107 回答