Мне нужно, чтобы мои приложения открывали документы из приложений Safari и Mail с помощью этой функции "Открыть в..." в классе UIDocumentInteractionController
. Как это сделать?
Поддержка пункта Open In... в моем приложении для iOS Mail And Safari
Ответ 1
Я знаю, что это было очень неприятно для меня как начинающего программиста или даже сейчас как опытного специалиста. Файловый ввод-вывод через приложения Mail и Safari включает в себя очень... интересно названные соглашения внутри самого приложения. Так что пусть наши руки грязные с проектом Xcode для iPhone. Откройте 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
Затем введите файл делегата .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 файл, который вы вытащили из любой точки (я рекомендую здесь, потому что кто не тратит свое свободное время на чтение огромных объемов документации?), затем скопируйте его название и вставьте его с удаленным суффиксом (.pdf); часть ofType:@"pdf"
позаботится об этом для нас. Строка должна выглядеть так, как только вы закончите: [[NSBundle mainBundle] pathForResource:@"//file name//" ofType:@"pdf"];
Теперь вернитесь в xib и подключите те IBOutlets
! Все сказано, вот что должно выглядеть ваша вкладка "Файл владельца":
Кажется, мы закончили... но подождите! Мы ничего не сделали, чтобы открыть меню "Открыть в..."! Ну, оказывается, что в файле .plist есть какая-то ошибка. Откройте приложение .plist(быстрый щелчок правой кнопкой мыши, затем выберите "Открыть как > Исходный код" ) и вставьте следующее:
<?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]
Если нужно щелкнуть правой кнопкой мыши и выбрать Open As > Property List, это будет выглядеть так:
Там еще ОЧЕНЬ важное поле в названии "Приложение поддерживает обмен файлами iTunes". Это должно быть установлено в "YES", или ваше приложение не будет отображаться в iTunes в качестве поддержки совместного использования файлов.
В поле "Типы документов" указаны типы документов, которые может открыть наш пример. Разверните стрелку, чтобы найти ее роль и UTI. Это уникальные идентификаторы (уникальные идентификаторы типов, кажется очевидным, что означает этот аббревиатура, не так ли?), Что каждый вид файла имеет. UTI - это то, что позволяет finder заменить общий образ документа с этим красивым локализованным изображением типа файла (не верьте мне, переименуйте несущественное расширение файла в .ouhbasdvluhb и попытайтесь получить красивое изображение!) Если бы я хотел открыть (скажем, файл .code), тогда я бы поставил что-то вроде com.CodaFi.code
(обратная DNS-запись для тех, у кого нет подсказки) в поле UTI, а Document Type Name будет "Document CodaFi". Ранг и роль обработчика должны быть понятны, поскольку наш ранг обработчика является альтернативным (потому что у нас нет файла), а наша роль - зритель (потому что нам не нужно ничего более важного. Наш пример - это просто средство просмотра, а не редактор, поэтому мы оставим это как таковое.
Для дальнейшего использования UTI имеет официальные системно объявленные схемы именования, когда они поступают из уважаемых источников (Oracle, Microsoft, даже самого Apple), которые можно найти в Справочное руководство по унифицированному типу имен, но перечислены здесь для радианта.
Теперь, пусть run 'er! Код должен строиться без ошибок, предполагая, что вы скопировали стенографию и получили эти проклятые xib-соединения. Теперь, когда вы впервые запускаете приложение, вам должна быть предоставлена возможность открыть документ в iBooks. Отмените выбор, реальное мясо кода открывает другие документы! Запустите Safari и найдите любой PDF файл, который Safari может открыть QuickLook или открыть. Затем в меню "Открыть в..." появится наше приложение! Нажмите на нее. Вы получите маленькую анимацию switcheroo, и появится предупреждение о местоположении файла. Когда вы отклоните его, UIWebView
загрузит PDF файл. Приложение Mail имеет аналогичную функциональность с вложениями. Вы также можете вызвать эти PDF файлы для своего приложения.
Что это, все сделано. Наслаждайтесь и счастливая кодировка!
Ответ 2
В этом вопросе есть отличный ответ . Я скопировал некоторые из нижеприведенных ответов для ясности, но вы должны обратиться к этому вопросу для получения полного ответа.
Обработка типов файлов является новой с 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>
В этом конкретном примере экспортируется com.sunsetlakesoftware.molecules.pdb
UTI с расширением файла .pdb, соответствующее типу MIME chemical/x-pdb
.
При этом ваше приложение сможет обрабатывать документы, прикрепленные к электронным письмам или другим приложениям в системе. В Mail вы можете нажать и удерживать, чтобы открыть список приложений, которые могут открыть конкретное вложение.
Когда приложение будет открыто, ваше приложение будет запущено, и вам нужно будет обработать обработку этого файла в вашем делетете делегата -application:didFinishLaunchingWithOptions:
. Похоже, что файлы, загруженные таким образом из Mail, копируются в ваш каталог "Документы" под подкаталогом, соответствующим тому, в каком почтовом ящике они прибыли.