我需要让我的应用程序打开Safari和邮件应用程序中带有"在…中打开"的文档UIDocumentInteractionController级的东西.我如何做到这一点?

推荐答案

我知道,作为一名初级程序员,甚至作为一名中等技能的程序员,这让我非常沮丧.通过邮件和Safari应用程序进行文件I/O涉及非常...有趣的是,应用程序本身中的命名约定.让我们用iPhone的Xcode项目来解决问题.打开Xcode(本教程我将使用4.2), Select "单一视图"应用程序模板(或者创建一个空项目,然后添加一个带有.xib的单一视图).

显示Xcode模板 Select 表的屏幕截图

在新创建的应用程序中,将视图控制器(以及相关的xib)重命名为OfflineReaderViewController,然后我们将开始讨论代码.(除了前缀header和main.m之外,我们将处理所有文件,所以请注意,您需要面前的一切!)

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

#import <UIKit/UIKit.h>

@class OfflineReaderViewController;

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic) OfflineReaderViewController *viewController;

@end

然后输入代表的姓名.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, 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

这是:

-(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是一个额外的功能,我们不会进入.

现在,我们必须布局我们的xib.输入xib(应标题为"OfflineReaderServiceController",但这与xib无关,除非我们调用initWithNibName:(我们不会这样做)),并使其看起来如下图所示:

IB布局截图

这是非常重要的,你进入UIWebView的属性,并判断"规模网页,以适应",因为这让我们放大和缩小网页与捏.暂时不要担心连接,我们很快就会创建这些连接.

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

#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.
    // 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

你们当中那些积极观看,而不仅仅是复制我告诉你们的一切(只是Jest )的人会知道,这一行:[[NSBundle mainBundle] pathForResource:@"Minore" ofType:@"pdf"];将给我们一个SIGABRT,因为,嗯,文件不存在!所以,把你从任何地方提取的任何通用PDF都拖进go (我推荐here个,因为谁不花空闲时间阅读大量文档?),然后复制它的标题,并将后缀(.pdf)删除后粘贴进go ;ofType:@"pdf"部分为我们解决了这个问题.完成后,线条应该是这样的:[[NSBundle mainBundle] pathForResource:@"//file name//" ofType:@"pdf"];

现在回到锡伯,把那IBOutlets个都挂上!总而言之,以下是"文件所有者"选项卡的外观:

显示已建立连接的屏幕截图

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>

[旁注:在任何plist的源代码中都要小心,如果你不知道自己在做什么,你可能会从Xcode中得到可怕的"此文件已损坏"错误]

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

Xcode plist编辑器窗口快照

还有一个非常重要的领域叫做"应用程序支持iTunes文件共享".必须设置为"是",否则你的应用程序将不会在iTunes中显示为支持文件共享.

"文档类型"字段指定我们的示例可以打开的文档类型.展开箭头以找到它的角色和UTI.这些是唯一标识符(唯一类型标识符;现在这个首字母缩写词的意思似乎很明显,不是吗?)每种文件都有.UTI让查找程序可以用文件类型的漂亮本地化图像替换通用文档图像(不要相信我,将一个不重要的文件扩展名重命名为.ouhbasdvluhb,并try 获得一张漂亮的图片!)如果我想打开自己的自定义格式(比如一个.code文件),那么我会在UTI字段中放入类似com.CodaFi.code的内容(对于那些没有线索的人来说是反向DNS标记),文档类型名称将是"CodaFi Document".处理程序的级别和角色应该很简单,因为我们的处理程序级别是备用的(因为我们不拥有文件),而我们的角色是查看器(因为我们不需要任何更重要的东西).我们的示例只是一个查看器,而不是一个编辑器,所以我们将其保持原样.

为了将来的参考,UTI的官方系统公布了命名方案,当它们来自受人尊敬的来源(甲骨文、微软,甚至苹果本身)时,它们可以在Uniform Type Identifier Reference Guide中找到,但出于迂腐的考虑,被列为here.

现在,让我们跑吧!假设你一字不差地复制了那些该死的xib连接,代码应该不会出错.现在,当您第一次启动应用程序时,您应该可以 Select 在iBooks中打开文档.取消 Select 它,代码的真正内容是打开其他文档!启动Safari并搜索Safari可以快速查看或打开的任何PDF.然后在"开放中…"菜单,我们的应用程序出现了!点击它.你会看到小的switcheroo动画,一个提示会显示文件的位置.当你撤销它时,UIWebView将加载PDF.邮件应用程序具有与附件类似的功能.你也可以将这些PDF文件调用到你的应用程序中.

就这样,一切都结束了.享受和快乐的编码!

Objective-c相关问答推荐

如果DISPATCH_SOURCE_SET_EVENT_HANDLER中的SELF需要为弱

目标 C:什么是(id)发件人?

Objective-C 中的实例变量是否默认设置为 nil?

iPhone 可达性判断

将谷歌 map 添加为子视图会使 iOS 应用程序因 exc_bad 崩溃

Xcode 是否支持区域?

用 UIActivityIndi​​catorView 替换 UIBarButtonItem

ARC的正确桥接?

使用自动布局以编程方式更改框架

使用 UIView animateWithDuration 进行动画处理时无法 UIButton

在运行时判断 iOS 版本?

[NSNull null] 和 nil 有什么区别?

iOS6:supportedInterfaceOrientations 不起作用(被调用但界面仍然旋转)

如何逐行从 NSFileHandle 读取数据?

NSCharacterSet:如何将_添加到 alphanumericCharacterSet 文本限制?

UILabel 根据要显示的文本自动调整大小

如何检测 NSString 是否为空?

@class 在 Objective-C 中做了什么?

UIView: opaque vs. alpha vs. opacity

无法从其 DataSource 获取单元格