支持iOS Mail和Safari应用程序中的Open In …菜单项

我需要使用UIDocumentInteractionController类中的“Open In …”事件,从Safari和Mail应用程序中打开我的应用程序。 我如何做到这一点?

我知道这对于我作为一名初级程序员来说是非常令人沮丧的,现在甚至是一个熟练的程序员。 通过邮件和Safari应用程序文件I / O涉及非常有趣的命名约定在应用程序本身。 所以让我们用iPhone的Xcode项目弄脏我们的手。 打开Xcode(我将在本教程中使用4.2),然后select“Single View”应用程序模板(或者创build一个空的项目,然后用.xib添加一个视图)。

显示Xcode模板选择表的屏幕截图

在这个新创build的应用程序中,将视图控制器(和关联的xib)重命名为OfflineReaderViewController ,然后我们将看到代码。 (我们会触摸每个文件,但是前缀头文件和main.m文件,所以要注意,你需要一切在你面前!)

inputAppDelegate标题并将以下代码粘贴到其中:

 #import <UIKit/UIKit.h> @class OfflineReaderViewController; @interface AppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) OfflineReaderViewController *viewController; @end 

然后input委托的.m文件并逐字粘贴下面的代码:

 #import "AppDelegate.h" #import "OfflineReaderViewController.h" @implementation AppDelegate @synthesize window; @synthesize viewController; -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { // Make sure url indicates a file (as opposed to, eg, http://) if (url != nil && [url isFileURL]) { // Tell our OfflineReaderViewController to process the URL [self.viewController handleDocumentOpenURL:url]; } // Indicate that we have successfully opened the URL return YES; } 
 - (void)dealloc { [window release]; [viewController release]; [super dealloc]; } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; // Override point for customization after application launch. self.viewController = [[[OfflineReaderViewController alloc] initWithNibName:@"ViewController" bundle:nil] autorelease]; self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; return YES; } - (void)applicationWillResignActive:(UIApplication *)application { /* Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. */ } - (void)applicationDidEnterBackground:(UIApplication *)application { /* Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. */ } - (void)applicationWillEnterForeground:(UIApplication *)application { /* Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. */ } - (void)applicationDidBecomeActive:(UIApplication *)application { /* Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. */ } - (void)applicationWillTerminate:(UIApplication *)application { /* Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. */ } @end 

这个:

 -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { if (url != nil && [url isFileURL]) { [self.viewController handleDocumentOpenURL:url]; } return YES; } 

是本教程中最重要的部分。 将其分解成各自的部分: -(BOOL)application:(UIApplication *)application是我们的示例应用程序; openURL:(NSURL *)url是发送的URL,告诉我们打开什么; sourceApplication:(NSString *)sourceApplication是发送链接的应用程序; 和annotation:(id)annotation是一个额外的function,我们不会介入。

现在,我们必须布置我们的xib。 inputxib(应该命名为“OfflineReaderViewController”,但是与xib无关,除非我们调用initWithNibName:我们不会)),并使其看起来像下图所示:

IB布局的屏幕截图

这是非常重要的,你进入UIWebView的属性,并检查“尺度页面适合”,因为这让我们放大和缩小在网页上捏。 不要担心这些关系,我们将很快创build这些关系。

inputOfflineReaderViewController标题并粘贴到以下内容中:

 #import <UIKit/UIKit.h> @interface OfflineReaderViewController : UIViewController <UIDocumentInteractionControllerDelegate> { IBOutlet UIWebView *webView; } -(void)openDocumentIn; -(void)handleDocumentOpenURL:(NSURL *)url; -(void)displayAlert:(NSString *) str; -(void)loadFileFromDocumentsFolder:(NSString *) filename; -(void)listFilesFromDocumentsFolder; - (IBAction) btnDisplayFiles; @end 

现在.m:

 #import "OfflineReaderViewController.h" @implementation OfflineReaderViewController UIDocumentInteractionController *documentController; -(void)openDocumentIn { NSString * filePath = [[NSBundle mainBundle] pathForResource:@"Minore" ofType:@"pdf"]; documentController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]]; documentController.delegate = self; [documentController retain]; documentController.UTI = @"com.adobe.pdf"; [documentController presentOpenInMenuFromRect:CGRectZero inView:self.view animated:YES]; } -(void)documentInteractionController:(UIDocumentInteractionController *)controller willBeginSendingToApplication:(NSString *)application { } -(void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application { } -(void)documentInteractionControllerDidDismissOpenInMenu: (UIDocumentInteractionController *)controller { } -(void) displayAlert:(NSString *) str { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:str delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; [alert release]; } - (void)handleDocumentOpenURL:(NSURL *)url { [self displayAlert:[url absoluteString]]; NSURLRequest *requestObj = [NSURLRequest requestWithURL:url]; [webView setUserInteractionEnabled:YES]; [webView loadRequest:requestObj]; } -(void)loadFileFromDocumentsFolder:(NSString *) filename { //---get the path of the Documents folder--- NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *filePath = [documentsDirectory stringByAppendingPathComponent:filename]; NSURL *fileUrl = [NSURL fileURLWithPath:filePath]; [self handleDocumentOpenURL:fileUrl]; } -(void)listFilesFromDocumentsFolder { //---get the path of the Documents folder--- NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSFileManager *manager = [NSFileManager defaultManager]; NSArray *fileList = [manager contentsOfDirectoryAtPath:documentsDirectory error:nil]; NSMutableString *filesStr = [NSMutableString stringWithString:@"Files in Documents folder \n"]; for (NSString *s in fileList){ [filesStr appendFormat:@"%@ \n", s]; } [self displayAlert:filesStr]; [self loadFileFromDocumentsFolder:@"0470918020.pdf"]; } - (IBAction) btnDisplayFiles { [self listFilesFromDocumentsFolder]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } #pragma mark - View lifecycle - (void)viewDidLoad { [super viewDidLoad]; [self openDocumentIn]; } - (void)viewDidUnload { [super viewDidUnload]; // Release any retained subviews of the main view. // eg self.myOutlet = nil; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); } @end 

那些正在积极关注而不是仅仅复制我告诉你(只是开玩笑)的人就知道这一行: [[NSBundle mainBundle] pathForResource:@"Minore" ofType:@"pdf"]; 会给我们一个SIGABRT,因为这个文件不存在! 那么,从任何地方(我推荐这里,因为谁不花费他们的空闲时间阅读大量的文档?)拖动任何通用的PDF,然后复制它的标题,并将其粘贴后缀(.pdf)删除; ofType:@"pdf"部分为我们照顾。 当你完成这一行时,这行应该看起来像这样: [[NSBundle mainBundle] pathForResource:@"//file name//" ofType:@"pdf"];

现在回到xib并连接那些IBOutlets ! 所有告诉,这是你的“文件的所有者”选项卡应该看起来像:

显示已建立的连接

看来我们已经完成了……但是等等! 我们没有做任何事情来获得“打开在…”菜单上运行! 那么,事实certificate,在.plist文件中有一些必要的东西。 打开应用程序的.plist(快速右键单击,然后select打开为>源代码)并粘贴到以下内容:

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleDevelopmentRegion</key> <string>en</string> <key>CFBundleDisplayName</key> <string>${PRODUCT_NAME}</string> <key>CFBundleExecutable</key> <string>${EXECUTABLE_NAME}</string> <key>CFBundleIconFiles</key> <array/> <key>CFBundleIdentifier</key> <string>CodaFi.${PRODUCT_NAME:rfc1034identifier}</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> <string>${PRODUCT_NAME}</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>1.0</string> <key>CFBundleSignature</key> <string>????</string> <key>CFBundleVersion</key> <string>1.0</string> <key>LSRequiresIPhoneOS</key> <true/> <key>UIRequiredDeviceCapabilities</key> <array> <string>armv7</string> </array> <key>UISupportedInterfaceOrientations</key> <array> <string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeRight</string> </array> <key>UIFileSharingEnabled</key> <true/> <key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeName</key> <string>PDF Document</string> <key>LSHandlerRank</key> <string>Alternate</string> <key>CFBundleTypeRole</key> <string>Viewer</string> <key>LSItemContentTypes</key> <array> <string>com.adobe.pdf</string> </array> </dict> </array> </dict> </plist> 

[注意:如果你不知道你在做什么,请仔细阅读任何plist源代码,你可以从Xcode中得到这个可怕的'This file has corrupted'错误]

如果要右键单击并select“打开为”>“属性列表”,则如下所示:

Xcode plist编辑器窗口的镜头

还有另一个非常重要的领域,称为“应用程序支持iTunes文件共享”。 必须将其设置为“YES”,否则您的应用程序将不会显示在iTunes中作为支持文件共享。

“文档types”字段指定了我们示例可以打开的文档种类。 展开箭头find它的angular色和UTI的。 这些都是唯一的标识符(唯一types标识符;似乎明显是什么缩写意味着现在,不是吗?),每种文件都有。 UTI是什么让查找replace一个通用的文件图像与文件types的好本地化形象(不要相信我,重命名一个不重要的文件扩展名为.ouhbasdvluhb,并试图得到一个不错的图片!)如果我想打开我的自己的自定义格式(可以说com.CodaFi.code文件),那么我会把类似com.CodaFi.code (对于那些没有线索的反向DNS符号)在UTI字段和文件types名称将是'CodaFi文件'。 处理程序的排名和angular色应该是直截了当的,因为我们的处理器级别是交替的(因为我们不拥有这个文件),我们的angular色是查看器(因为我们不需要更重要的东西,我们的例子只是一个查看器而不是编辑器,所以我们就这样离开。

为了将来的参考,UTI有正式的系统声明的命名scheme,当他们来自尊重的来源(甲骨文,微软,甚至苹果本身),可以在统一types标识符参考指南中find ,但这里列出为了pedantry。

现在,让我们运行'呃! 代码应该没有错误地build立,假设你逐字复制,并得到那些dib的xib连接正确。 现在,当您第一次启动您的应用程序时,您应该可以select在iBooks中打开文档。 取消select它,代码的真正的肉是打开其他文件! 启动Safari并searchSafari可以快速查找或打开的任何PDF。 然后在“打开在…”菜单中,我们的应用程序显示! 点击它。 你会得到小的switcherooanimation和一个警报将拿出文件的位置。 当你解雇它时, UIWebView将加载PDF。 邮件应用程序具有与附件类似的function。 您也可以将这些PDF文件直接发送到您的应用程序。

就这样,一切都完成了。 享受和愉快的编码!

这里有一个很好的答案。 为了清楚起见,我已经复制了下面的一些答案,但是您应该参考该问题以获得完整的答案。

文件types处理是iPhone OS 3.2的新增function,与已有的自定义URLscheme不同。 您可以注册您的应用程序来处理特定的文档types,任何使用文档控制器的应用程序都可以将这些文档的处理交给您自己的应用程序。

要注册支持,您需要在Info.plist中具有以下内容:

 <key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeIconFiles</key> <array> <string>Document-molecules-320.png</string> <string>Document-molecules-64.png</string> </array> <key>CFBundleTypeName</key> <string>Molecules Structure File</string> <key>CFBundleTypeRole</key> <string>Viewer</string> <key>LSHandlerRank</key> <string>Owner</string> <key>LSItemContentTypes</key> <array> <string>com.sunsetlakesoftware.molecules.pdb</string> <string>org.gnu.gnu-zip-archive</string> </array> </dict> </array> 

上例中使用的一个UTI是系统定义的,但另一个是特定于应用程序的UTI。 应用程序特定的UTI将需要导出,以便系统上的其他应用程序可以知道它。 要做到这一点,你可以在Info.plist中添加一个部分,如下所示:

 <key>UTExportedTypeDeclarations</key> <array> <dict> <key>UTTypeConformsTo</key> <array> <string>public.plain-text</string> <string>public.text</string> </array> <key>UTTypeDescription</key> <string>Molecules Structure File</string> <key>UTTypeIdentifier</key> <string>com.sunsetlakesoftware.molecules.pdb</string> <key>UTTypeTagSpecification</key> <dict> <key>public.filename-extension</key> <string>pdb</string> <key>public.mime-type</key> <string>chemical/x-pdb</string> </dict> </dict> </array> 

这个特殊的例子导出com.sunsetlakesoftware.molecules.pdb UTI与.pdb文件扩展名相对应的MIMEtypeschemical/x-pdb

有了这个,您的应用程序将能够处理附加到电子邮件或从系统上的其他应用程序的文件。 在Mail中,您可以点击并按住以打开可打开特定附件的应用程序列表。

打开附件时,您的应用程序将启动,您将需要在-application:didFinishLaunchingWithOptions: application委托方法中处理此文件的处理。 看起来,以这种方式从Mail加载的文件被复制到您的应用程序的Documents目录下与其到达的电子邮箱相对应的子目录下。