Jan*_*Jan 7 cocoa-touch objective-c uti ios
如何知道应用支持哪种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]
Run Code Online (Sandbox Code Playgroud)
它显示了受支持的应用程序,但是,如果我选择它,它将不会切换应用程序.代表打电话给
- (void)documentInteractionController:(UIDocumentInteractionController *)controller willBeginSendingToApplication:(NSString *)application
Run Code Online (Sandbox Code Playgroud)
但
- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application
Run Code Online (Sandbox Code Playgroud)
永远不会被调用,应用程序永远不会切换.
目标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>
Run Code Online (Sandbox Code Playgroud)
由于这不起作用,我还尝试添加自定义扩展.不过,它不会以这种方式起作用.将自定义扩展添加到文件时,我将其移交给它DocumentInteractionController
并且它可以正常工作.但是,无论我提供的UTI类型如何,应用程序列表都会显示支持相同文件扩展名的所有其他应用程序.
假设我在2个不同的应用程序中声明2个UTI:
App1 with UTI1: com.mycomp.a with extension .abc
App2 with UTI2: com.mycomp.b with extension .abc
Run Code Online (Sandbox Code Playgroud)
将文件传递给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>
Run Code Online (Sandbox Code Playgroud)
我真的很感谢你的帮助,我有点被困住了.所以,问题是:如何将文件发送到具有已知UTI的应用程序,如果没有扩展名,或者与其他文件具有相同的扩展名,我不想在DocumentInteractionController中将应用程序显示为选项?
谢谢
我找到了解决这个问题的办法。不过,我认为这并不是一件很好的事情。
在测试过程中,我发现当保留文件扩展名时,UIDocumentInteractionController
将根据UTI
我指定的应用程序显示应用程序。将文件发送到目标应用程序时不会发生任何事情。我的结论是我需要一个文件扩展名来完成最终的发送。
URL
我的方法是在将文件发送到目标应用程序之前修改属性,并向其提供相同的文件,但具有目标应用程序接受的文件扩展名。尽管如此,我的应用程序还是崩溃了。我用 Instruments 对其进行了分析,发现问题是由于UIDocumentInteractionController
过度释放某些代理对象造成的。我还看到最终的过度释放是在一个名为类别_invalidate
的方法中UIDocumentInteractionController(Private)
调用的,该方法释放了对象。
由于类别不能被其他类别覆盖,我决定将类别方法与我自己的实现混合在一起,检查 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
Run Code Online (Sandbox Code Playgroud)
_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];
Run Code Online (Sandbox Code Playgroud)
此代码显示了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]);
}
Run Code Online (Sandbox Code Playgroud)
这个解决方案有效,但在我看来这是一个肮脏的黑客,必须有更好的解决方案。