Я знаю, что это было крайне неприятно для меня, как для начинающего программиста, или даже для человека с умеренными навыками сейчас. Файловый ввод / вывод через приложения Mail и Safari включает в себя очень ... интересно названные соглашения внутри самого приложения. Итак, давайте запачкаем руки проектом Xcode для iPhone. Откройте XCode (я буду использовать 4.2 для этого урока) и выберите шаблон приложения «Single View» (или создайте пустой проект, затем добавьте одиночное представление с расширением .xib).
![Screenshot showing Xcode template selection sheet](https://i.stack.imgur.com/2OSRI.png)
В этом недавно созданном приложении переименуйте контроллер представления (и связанный с ним 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:
(что мы не будем)) и сделаем так, как показано на рисунке ниже:
![Screenshot of IB layout](https://i.stack.imgur.com/GH4PR.png)
ОЧЕНЬ важно, чтобы вы вошли в атрибуты 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
! В общем, вот как должна выглядеть ваша вкладка «Владелец файла»:
![Screenshot showing established connections](https://i.stack.imgur.com/PW29H.png)
Кажется, мы закончили ... но подождите! Мы ничего не сделали, чтобы открыть и запустить меню «Открыть в ...»! Что ж, получается, что в файле .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>
[Примечание: будьте осторожны, осматривая исходный код любого списка, если вы не знаете, что делаете, вы можете получить страшную ошибку «Этот файл поврежден» из Xcode]
Если бы щелкнуть правой кнопкой мыши и выбрать «Открыть как»> «Список свойств», это выглядело бы так:
![Shot of Xcode plist editor window](https://i.stack.imgur.com/avV2R.png)
Там есть еще одно ОЧЕНЬ важное поле, которое называется «Приложение поддерживает общий доступ к файлам iTunes». Для него должно быть установлено значение «ДА», иначе ваше приложение не будет отображаться в iTunes как поддерживающее общий доступ к файлам.
В поле «Типы документов» указываются типы документов, которые можно открыть в нашем примере. Разверните стрелку, чтобы найти ее роль и UTI. Это уникальные идентификаторы (Unique Type Identifiers; кажется очевидным, что сейчас означает эта аббревиатура, не так ли?), Которые есть у каждого типа файлов. UTI - это то, что позволяет поисковику заменить общее изображение документа на это красивое локализованное изображение типа файла (не верьте мне, переименуйте неважное расширение файла в .ouhbasdvluhb и попробуйте получить красивую картинку!) Если бы я хотел открыть свой свой собственный формат (скажем, файл .code), тогда я бы поместил что-то вроде com.CodaFi.code
(обратная DNS-запись для тех, у кого нет подсказок) в поле UTI, а имя типа документа будет «CodaFi Document». Ранг и роль обработчика должны быть простыми, поскольку наш ранг обработчика является альтернативным (поскольку мы не являемся владельцем файла), а наша роль - средство просмотра (потому что нам не нужно ничего более важного. Наш пример - просто средство просмотра, а не редактор, поэтому мы оставим это так.
Для справки в будущем у UTI есть официальные объявленные системой схемы именования, когда они поступают из уважаемых источников (Oracle, Microsoft, даже сама Apple), которые можно найти в Справочном руководстве по унифицированному идентификатору типа , но перечислено здесь ради педантизма.
Теперь давай побежим! Код должен собираться без ошибок, при условии, что вы скопировали дословно и правильно выполнили эти проклятые xib-соединения. Теперь, когда вы впервые запускаете приложение, вам должна быть предоставлена возможность открыть документ в iBooks. Отмените выбор, настоящая суть кода - открытие других документов! Запустите Safari и найдите любой PDF-файл, который Safari может открыть QuickLook или открыть. Затем в меню «Открыть в ...» наше приложение появляется! Нажмите на это. Вы получите небольшую анимацию переключения и появится предупреждение о местонахождении файла. Когда вы отклоните его, UIWebView
загрузит PDF. Почтовое приложение имеет схожую функциональность с вложениями. Вы также можете вызвать эти PDF-файлы до вашего приложения.
Вот и все, все сделано. Наслаждайтесь и счастливого кодирования!