objective-c - support - mac mail app推薦




在我的iOS Mail和Safari應用程序中支持Open In...菜單項 (2)

我知道這對我來說是一個非常令人沮喪的初級程序員,現在甚至是一個技術熟練的程序員。 通過Mail和Safari應用程序的文件I / O涉及應用程序本身內非常有趣的命名約定。 因此,讓我們親自動手使用適用於iPhone的Xcode項目。 打開Xcode(我將在本教程中使用4.2)並選擇“單一視圖”應用程序模板(或創建一個空項目,然後使用.xib添加單個視圖)。

在新創建的應用程序中,將視圖控制器(和關聯的xib)重命名為OfflineReaderViewController ,然後我們將開始使用代碼。 (我們將觸摸除前綴標題和main.m之外的所有文件,因此請注意,您需要在您面前的所有內容!)

輸入AppDelegate標頭並將以下代碼粘貼到其中:

#import <UIKit/UIKit.h>

@class OfflineReaderViewController;

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic) OfflineReaderViewController *viewController;

@end

然後輸入Delegate的.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(應該標題為'OfflineReaderViewController',但它與xib無關,除非我們調用initWithNibName:我們不會)),並使其如下圖所示:

進入UIWebView的屬性並選中“Scales Pages To Fit”非常重要,因為這樣我們就可以通過捏合來放大和縮小網頁。 暫時不要擔心連接,我們很快就會創建它們。

輸入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

那些正在積極觀看並且不僅僅是複制我告訴你的所有內容(開玩笑)的人都知道這一行: [[NSBundle mainBundle] pathForResource:@"Minore" ofType:@"pdf"]; 將給我們一個SIGABRT,因為,該文件不存在! 那麼,拖動你從哪里拉出的任何通用PDF(我here推薦here因為誰沒有花時間閱讀大量的文檔?),然後復制它的標題並將其粘貼,後綴(.pdf)被刪除; ofType:@"pdf"部分為我們處理。 當你完成它時,該行應如下所示: [[NSBundle mainBundle] pathForResource:@"//file name//" ofType:@"pdf"];

現在回到xib並連接那些IBOutlets ! 總而言之,這是你的“文件所有者”標籤應該是這樣的:

看來我們已經完成了......但是等等! 我們沒有做任何事情來啟動和運行“打開...”菜單! 好吧,事實證明,必須在.plist文件中存在一些問題。 打開app .plist(快速右鍵單擊,然後選擇Open As> Source Code)並粘貼以下內容:

<?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的錯誤]

如果要右鍵單擊並選擇打開方式>屬性列表,它將如下所示:

還有一個非常重要的領域叫做“應用程序支持iTunes文件共享”。 必須設置為“YES”,否則您的應用程序將不會在iTunes中顯示為支持文件共享。

“文檔類型”字段指定了我們的示例可以打開的文檔類型。 展開箭頭以查找其角色和UTI。 這些是每種文件都有的唯一標識符(唯一類型標識符;現在看起來很明顯是什麼縮寫,不是嗎?)。 UTI是讓發現者用文件類型的優秀本地化圖像替換通用文檔圖像的東西(不要相信我,將一個不重要的文件擴展名重命名為.ouhbasdvluhb並嘗試獲得一個漂亮的圖片!)如果我想打開我的自己的自定義格式(比如一個.code文件)然後我會在UTI字段中輸入類似com.CodaFi.code (對於那些沒有線索的反向DNS表示法),文檔類型名稱將是“CodaFi Document”。 處理程序等級和角色應該是直截了當的,因為我們的處理程序等級是備用的(因為我們不擁有文件)而我們的角色是查看器(因為我們不需要任何更重要的東西。我們的示例只是一個查看器而不是編輯器,所以我們會這樣離開。

為了供將來參考,UTI有來自受尊敬的來源(甲骨文,微軟,甚至是蘋果本身)的官方系統聲明的命名方案,可以在“ 統一類型標識符參考指南”中找到 ,但為了迂實而列here

現在,讓我們跑吧! 代碼應該構建沒有錯誤,假設你逐字複製並使那些darned xib連接正確。 現在,當您首次啟動應用程序時,應該會看到在iBooks中打開文檔的選項。 取消選擇它,代碼的真正含義是打開其他文檔! 啟動Safari並蒐索Safari可以QuickLook或打開的任何PDF。 然後在“打開...”菜單中,我們的應用程序顯示出來! 點擊它。 你會得到一個小的switchheroo動畫,一個警報會提出文件的位置。 當您關閉它時, UIWebView將加載PDF。 Mail應用程序具有與附件類似的功能。 您也可以將這些PDF調用到您的應用程序。

就是這樣,一切都完成了。 享受和快樂的編碼!

我需要讓我的應用程序打開來自Safari和Mail應用程序的文檔,並在UIDocumentInteractionController類中使用“Open In ...”。 我該如何做到這一點?


這裡有一個很好的答案。 為了清楚起見,我已經復制了下面的一些答案,但你應該參考那個問題來獲得完整的答案。

文件類型處理是iPhone OS 3.2的新增功能,與現有的自定義URL方案不同。 您可以註冊應用程序以處理特定的文檔類型,使用文檔控制器的任何應用程序都可以將這些文檔的處理交給您自己的應用程序。

要註冊支持,您需要在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>

此特定示例使用.pdb文件擴展名導出com.sunsetlakesoftware.molecules.pdb UTI,對應於MIME類型chemical/x-pdb

有了這個,您的應用程序將能夠處理附加到電子郵件或系統上其他應用程序的文檔。 在Mail中,您可以點按並按住以顯示可以打開特定附件的應用程序列表。

打開附件後,您的應用程序將啟動,您需要在-application:didFinishLaunchingWithOptions:處理此文件-application:didFinishLaunchingWithOptions: application delegate方法。 看來,以這種方式從Mail加載的文件被複製到應用程序的Documents目錄下,該子目錄對應於它們到達的電子郵箱。







uidocumentinteraction