支持在我的 iOS 邮件和 Safari 应用程序中打开...菜单项 [英] Supporting Open In... menu item in my app for iOS Mail And Safari

查看:24
本文介绍了支持在我的 iOS 邮件和 Safari 应用程序中打开...菜单项的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要使用 UIDocumentInteractionController 类中的打开方式..."来让我的应用程序打开来自 Safari 和邮件应用程序的文档.我该如何实现?

解决方案

我知道这对我作为一名初级程序员,甚至现在作为一个中等技能的程序员来说都非常令人沮丧.通过 Mail 和 Safari 应用程序进行的文件 I/O 涉及非常……有趣的应用程序本身命名约定.因此,让我们动手做一个 iPhone 的 Xcode 项目.打开 Xcode(我将在本教程中使用 4.2)并选择单一视图"应用程序模板(或创建一个空项目,然后添加一个带有 .xib 的单一视图).

在那个新创建的应用程序中,将视图控制器(和关联的 xib)重命名为 OfflineReaderViewController,然后我们将开始编写代码.(除了前缀头文件和 main.m 之外,我们将触及每个文件,因此请注意,您将需要前面的所有内容!)

输入 AppDelegate 标头并将以下代码粘贴到其中:

#import @class OfflineReaderViewController;@interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window;@property (strong, nonatomic) OfflineReaderViewController *viewController;@结尾

然后输入 Delegate 的 .m 文件并逐字粘贴以下代码:

#import "AppDelegate.h"#import "OfflineReaderViewController.h"@implementation AppDelegate@合成窗口;@synthesize 视图控制器;-(BOOL)application:(UIApplication *)applicationopenURL:(NSURL *)url源应用程序:(NSString *)源应用程序注释:(id)注释{//确保 url 表示一个文件(而不是,例如,http://)if (url != nil && [url isFileURL]) {//告诉我们的 OfflineReaderViewController 处理 URL[self.viewController handleDocumentOpenURL:url];}//表示我们已经成功打开了URL返回是;}

- (void)dealloc{【窗口发布】;[viewController 发布];[超级dealloc];}- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];//在应用程序启动后覆盖自定义点.self.viewController = [[[OfflineReaderViewController alloc] initWithNibName:@"ViewController" bundle:nil] autorelease];self.window.rootViewController = self.viewController;[self.window makeKeyAndVisible];返回是;}- (void)applicationWillResignActive:(UIApplication *)application{/*当应用程序即将从活动状态变为非活动状态时发送.这可能发生在某些类型的临时中断(例如来电或 SMS 消息)或用户退出应用程序并开始转换到后台状态时.使用此方法可暂停正在进行的任务、禁用计时器并降低 OpenGL ES 帧速率.游戏应该使用这种方法来暂停游戏.*/}- (void)applicationDidEnterBackground:(UIApplication *)application{/*使用此方法可以释放共享资源、保存用户数据、使计时器失效并存储足够的应用程序状态信息,以便在应用程序稍后终止时将应用程序恢复到当前状态.如果您的应用程序支持后台执行,则在用户退出时调用此方法而不是 applicationWillTerminate:.*/}- (void)applicationWillEnterForeground:(UIApplication *)application{/*作为从后台到活动状态过渡的一部分调用;在这里,您可以撤消在进入后台时所做的许多更改.*/}- (void)applicationDidBecomeActive:(UIApplication *)application{/*重新启动应用程序处于非活动状态时暂停(或尚未启动)的任何任务.如果应用程序之前在后台,可选择刷新用户界面.*/}- (void)applicationWillTerminate:(UIApplication *)application{/*在应用程序即将终止时调用.如果合适,保存数据.另请参阅 applicationDidEnterBackground:.*/}@结尾

这个:

-(BOOL)application:(UIApplication *)applicationopenURL:(NSURL *)url源应用程序:(NSString *)源应用程序注释:(id)注释{if (url != nil && [url isFileURL]) {[self.viewController handleDocumentOpenURL:url];}返回是;}

是本教程中最重要的部分.将其分解为各自的部分: -(BOOL)application:(UIApplication *)application 是我们的示例应用程序;openURL:(NSURL *)url 是用来告诉我们要打开什么的 URL;sourceApplication:(NSString *)sourceApplication 是发送链接的应用程序;而 annotation:(id)annotation 是我们不会涉及的额外功能.

现在,我们必须布置我们的 xib.输入xib(它应该被命名为'OfflineReaderViewController',但它与xib无关,除非我们调用initWithNibName:(我们不会)),并使它看起来像图片下面:

进入 UIWebView 的属性并选中缩放页面以适合"非常重要,因为这让我们可以放大和缩小网页.暂时不要担心连接,我们很快就会创建这些连接.

输入 OfflineReaderViewController 标题并粘贴以下内容:

#import @interface OfflineReaderViewController : UIViewController{IBOutlet UIWebView *webView;}-(void)openDocumentIn;-(void)handleDocumentOpenURL:(NSURL *)url;-(void)displayAlert:(NSString *) str;-(void)loadFileFromDocumentsFolder:(NSString *) 文件名;-(void)listFilesFromDocumentsFolder;- (IBAction) btnDisplayFiles;@结尾

现在是 .m:

#import "OfflineReaderViewController.h"@implementation OfflineReaderViewControllerUIDocumentInteractionController *documentController;-(void)openDocumentIn {NSString * 文件路径 =[[NSBundle mainBundle]pathForResource:@"Minore" ofType:@"pdf"];文档控制器 =[UIDocumentInteractionController interactControllerWithURL:[NSURL fileURLWithPath:filePath]];documentController.delegate = self;[documentController 保留];documentController.UTI = @"com.adobe.pdf";[documentController presentOpenInMenuFromRect:CGRectZeroinView:self.view动画:是];}-(void)documentInteractionController:(UIDocumentInteractionController *)controllerwillBeginSendingToApplication:(NSString *)application {}-(void)documentInteractionController:(UIDocumentInteractionController *)controllerdidEndSendingToApplication:(NSString *)application {}-(void)documentInteractionControllerDidDismissOpenInMenu:(UIDocumentInteractionController *) 控制器 {}-(void) displayAlert:(NSString *) str {UIAlertView *警报=[[UIAlertView alloc] initWithTitle:@"Alert"消息:str委托:自己取消按钮标题:@"好的"otherButtonTitles:nil];[警报显示];[警报发布];}- (void)handleDocumentOpenURL:(NSURL *)url {[self displayAlert:[url absoluteString]];NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];[webView setUserInteractionEnabled:YES];[webView loadRequest:requestObj];}-(void)loadFileFromDocumentsFolder:(NSString *) 文件名 {//--- 获取 Documents 文件夹的路径---NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, 是);NSString *documentsDirectory = [paths objectAtIndex:0];NSString *filePath = [documentsDirectorystringByAppendingPathComponent:filename];NSURL *fileUrl = [NSURL fileURLWithPath:filePath];[self handleDocumentOpenURL:fileUrl];}-(void)listFilesFromDocumentsFolder {//--- 获取 Documents 文件夹的路径---NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, 是);NSString *documentsDirectory = [paths objectAtIndex:0];NSFileManager *manager = [NSFileManager defaultManager];NSArray *fileList =[manager contentsOfDirectoryAtPath:documentsDirectory 错误:nil];NSMutableString *filesStr =[NSMutableString stringWithString:@"文档文件夹中的文件
"];for (NSString *s in fileList){[filesStr appendFormat:@"%@ 
", s];}[self displayAlert:filesStr];[self loadFileFromDocumentsFolder:@"0470918020.pdf"];}- (IBAction) btnDisplayFiles {[self listFilesFromDocumentsFolder];}- (void)didReceiveMemoryWarning{[超级didReceiveMemoryWarning];//释放任何未使用的缓存数据、图像等.}#pragma mark - 查看生命周期- (void)viewDidLoad {[超级viewDidLoad];[self openDocumentIn];}- (void)viewDidUnload{[超级viewDidUnload];//释放主视图的所有保留子视图.//例如self.myOutlet = nil;}- (void)viewWillAppear:(BOOL) 动画{[super viewWillAppear:animated];}- (void)viewDidAppear:(BOOL)动画{[super viewDidAppear:animated];}- (void)viewWillDisappear:(BOOL)动画{[super viewWillDisappear:animated];}- (void)viewDidDisappear:(BOOL)动画{[super viewDidDisappear:animated];}- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{//为支持的方向返回 YESreturn (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);}@结尾

那些积极观看而不只是复制我告诉你的所有内容(开玩笑)的人会知道这一行:[[NSBundle mainBundle] pathForResource:@"Minore" ofType:@"pdf"]; 会给我们一个 SIGABRT 因为,好吧,该文件不存在!因此,将您从任何地方提取的任何通用 PDF 拖入(我推荐 ,但在这里 为学究的清酒.

现在,让我们跑吧!假设您逐字复制并正确设置了那些该死的 xib 连接,代码应该没有错误地构建.现在,当您第一次启动应用程序时,您应该会看到在 iBooks 中打开文档的选项.取消选择它,代码的真正内容是打开其他文件!启动 Safari 并搜索 Safari 可以 QuickLook 或打开的任何 PDF.然后在打开方式..."菜单中,我们的应用程序出现了!点击它.您将获得小切换动画,并且会出现有关文件位置的警报.当您关闭它时,UIWebView 将加载 PDF.邮件应用程序具有与附件类似的功能.您还可以将这些 PDF 调用到您的应用程序.

就是这样,一切都完成了.享受和快乐编码!

I need to have my app open documents from the Safari and Mail apps with that "Open In..." thing in the UIDocumentInteractionController class. How do I accomplish this?

解决方案

I know this was extremely frustrating for me as a beginning programmer, or even as a moderately skilled one now. File I/O through the Mail and Safari apps involves very... interestingly named conventions within the app itself. So let's get our hands dirty with an Xcode project for iPhone. Open Xcode (I will be using 4.2 for this Tutorial) and select the 'Single View' application template (or create an empty project, then add a single view with a .xib).

In that newly created application, rename the view controller (and associated xib) to OfflineReaderViewController, and then we'll get down to the code. (We will touch every file but the prefix header and main.m, so be aware that you'll need everything in front of you!)

Enter the AppDelegate header and paste the following code into it:

#import <UIKit/UIKit.h>

@class OfflineReaderViewController;

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic) OfflineReaderViewController *viewController;

@end

Then enter the Delegate's .m file and paste the following code in verbatim:

#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, e.g., 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 active 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

This:

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

Is the singular most important part of this tutorial. To break it down into its respective parts: -(BOOL)application:(UIApplication *)application is our sample app; openURL:(NSURL *)url is the URL that's sent to tell us what to open; sourceApplication:(NSString *)sourceApplication is the application that sent the link; and annotation:(id)annotation is an extra feature we won't get into.

Now, we must layout our xib. Enter the xib (which should be entitled 'OfflineReaderViewController', but it doesn't matter with a xib, unless we call initWithNibName: (which we won't)), and make it look like the picture below:

It is VERY important that you go into the UIWebView's Attributes and check "Scales Pages To Fit", as this let's us zoom in and out on web pages with pinches. Don't worry about the connections just yet, we will be creating those shortly.

Enter the OfflineReaderViewController header and paste in the following:

#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

Now the .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 
"];
    for (NSString *s in fileList){    
        [filesStr appendFormat:@"%@ 
", 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.
    // e.g. 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

Those of you who are actively watching and not just copying everything I tell you to (just kidding) will know that this line: [[NSBundle mainBundle] pathForResource:@"Minore" ofType:@"pdf"]; will give us a SIGABRT because, well, the file doesn't exist! So, drag in any generic PDF that you've pulled from wherever (I recommend here because who doesnt spend their free time reading massive amounts of documentation?), then copy its title and paste it in with the suffix (.pdf) removed; the ofType:@"pdf" part takes care of that for us. The line should look like this when you're done with it: [[NSBundle mainBundle] pathForResource:@"//file name//" ofType:@"pdf"];

Now go back into the xib and hook up those IBOutlets! All told, here's what your "File's owner" tab should look like:

It seems we're done...but wait! We didn't do anything to get an "Open In..." menu up and running! Well, it turns out that there is some mucking around in the .plist file necessary. Open up the app .plist (with a quick right click, then select Open As > Source Code) and paste in the following:

<?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>

[Side note: be careful mucking around in the source code of any plist, if you don't know what you're doing, you could get the dreaded 'This file has been corrupted' error from Xcode]

If one were to right click and select Open As > Property List, it would look like this:

There's another VERY important field in there called 'Application supports iTunes file sharing'. That must be set to "YES", or your app will not show up in iTunes as supporting file sharing.

The 'Document Types' field specifies the kinds of documents our example can open. Expand the arrow to find its role and UTI's. These are unique identifiers (Unique Type Identifiers; seems obvious what that acronym means now, doesn't it?) that every kind of file has. UTI's are what let the finder replace a generic document image with that nice localized image of the file type (don't believe me, rename an unimportant file extension to .ouhbasdvluhb and try to get a nice picture!) If I wanted to open my own custom format (lets say a .code file) then I would put something like com.CodaFi.code (reverse DNS notation for those with no clue) in the UTI field and Document Type Name would be 'CodaFi Document'. Handler Rank and Role should be straightforward as our handler rank is alternate (because we don't own the file) and our role is viewer (because we don't need anything more important. Our example is just a viewer and not an editor, so we'll leave it as such.

For future reference, UTI's have official system-declared naming schemes when they come from respected sources (Oracle, Microsoft, even Apple itself) which can be found in the Uniform Type Identifier Reference Guide, but are listed here for pedantry's sake.

Now, let's run 'er! The code should build with no errors, assuming you copied verbatim and got those darned xib hookups right. Now when you first launch your application, you should be presented with the option to open a document in iBooks. Deselect it, the real meat of the code is opening other documents! Launch Safari and search for any PDF that Safari can QuickLook or open. Then in the "Open in..." menu, our app shows up! Click it. You'll get the little switcheroo animation and an alert will come up with the location of the file. When you dismiss it, the UIWebView will have loaded the PDF. The Mail app has similar functionality with attachments. You can also call those PDFs up to your app.

That's it, it's all done. Enjoy and happy coding!

这篇关于支持在我的 iOS 邮件和 Safari 应用程序中打开...菜单项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆