objective c - bar - Compatibilidad con el elemento de menú Abrir en... en mi aplicación para iOS Mail And Safari
apple-mobile-web-app-status-bar-style (2)
Necesito que mi aplicación abra documentos desde las aplicaciones Safari y Mail con esa cosa "Abrir en ..." en la clase UIDocumentInteractionController
. ¿Cómo logro esto?
Aquí hay una excelente respuesta a esta pregunta. He copiado parte de la respuesta a continuación para mayor claridad, pero debe consultar esa pregunta para obtener la respuesta completa.
El manejo del tipo de archivo es nuevo con iPhone OS 3.2 y es diferente de los esquemas de URL personalizados ya existentes. Puede registrar su aplicación para manejar tipos de documentos particulares, y cualquier aplicación que use un controlador de documentos puede transferir el procesamiento de estos documentos a su propia aplicación.
Para registrar soporte, deberá tener algo como lo siguiente en su 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>
Una de las UTI utilizadas en el ejemplo anterior estaba definida por el sistema, pero la otra era una UTI específica de la aplicación. La UTI específica de la aplicación deberá exportarse para que otras aplicaciones en el sistema puedan ser conscientes de ello. Para hacer esto, debería agregar una sección a su Info.plist como la siguiente:
<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>
Este ejemplo particular exporta la UTI com.sunsetlakesoftware.molecules.pdb
con la extensión de archivo .pdb, que corresponde al tipo MIME chemical/x-pdb
.
Con esto en su lugar, su aplicación podrá manejar documentos adjuntos a correos electrónicos o desde otras aplicaciones en el sistema. En Correo, puede tocar y mantener para mostrar una lista de aplicaciones que pueden abrir un archivo adjunto en particular.
Cuando se abra el archivo adjunto, su aplicación se iniciará y tendrá que manejar el procesamiento de este archivo en su -application:didFinishLaunchingWithOptions:
método delegado de la aplicación. Parece que los archivos cargados de este modo desde el Correo se copian en el directorio Documentos de la aplicación en un subdirectorio correspondiente a la casilla de correo electrónico en la que llegaron.
Sé que esto fue extremadamente frustrante para mí como programador principiante, o incluso como moderadamente hábil ahora. El archivo de E / S a través de las aplicaciones de correo y Safari involucra muy ... interesantes convenciones dentro de la propia aplicación. Así que vamos a ensuciarnos las manos con un proyecto de Xcode para iPhone. Abra Xcode (voy a usar 4.2 para este Tutorial) y seleccione la plantilla de la aplicación ''Vista única'' (o cree un proyecto vacío, luego agregue una vista única con .xib).
En esa aplicación recién creada, cambie el nombre del controlador de vista (y el xib asociado) a OfflineReaderViewController
, y luego veremos el código. (Tocaremos todos los archivos, excepto el encabezado del prefijo y main.m, así que ten en cuenta que necesitarás todo lo que tienes delante).
Ingrese el encabezado AppDelegate y pegue el siguiente código en él:
#import <UIKit/UIKit.h>
@class OfflineReaderViewController;
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) OfflineReaderViewController *viewController;
@end
Luego ingrese el archivo .m del Delegado y pegue el siguiente código en forma literal:
#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
Esta:
-(BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
if (url != nil && [url isFileURL]) {
[self.viewController handleDocumentOpenURL:url];
}
return YES;
}
Es la parte más importante de este tutorial. Para -(BOOL)application:(UIApplication *)application
en sus partes respectivas: -(BOOL)application:(UIApplication *)application
es nuestra aplicación de muestra; openURL:(NSURL *)url
es la URL que se envía para decirnos qué abrir; sourceApplication:(NSString *)sourceApplication
es la aplicación que envió el enlace; y annotation:(id)annotation
es una característica adicional que no entraremos.
Ahora, debemos diseñar nuestro xib. Ingrese el xib (que debería titularse ''OfflineReaderViewController'', pero no importa con un xib, a menos que llamemos a initWithNibName:
(que no lo hagamos)), y haga que se vea como la imagen siguiente:
Es MUY importante que vaya a los UIWebView
de UIWebView
y marque "Ajustar las páginas para ajustar", ya que esto nos permite acercar y alejar las páginas web con pellizcos. No se preocupe por las conexiones, las crearemos en breve.
Ingrese el encabezado OfflineReaderViewController
y pegue lo siguiente:
#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
Ahora el .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
Aquellos de ustedes que están mirando activamente y no simplemente copiando todo lo que les digo (es broma) sabrán que esta línea: [[NSBundle mainBundle] pathForResource:@"Minore" ofType:@"pdf"];
nos dará un SIGABRT porque, bueno, ¡el archivo no existe! Entonces, arrastre en cualquier PDF genérico que haya sacado de donde sea (lo recomiendo here porque ¿quién no pasa su tiempo libre leyendo cantidades masivas de documentación?), Luego copie su título y péguelo con el sufijo (.pdf) eliminado; la parte ofType:@"pdf"
se ocupa de eso para nosotros. La línea debería verse así cuando haya terminado con ella: [[NSBundle mainBundle] pathForResource:@"//file name//" ofType:@"pdf"];
¡Ahora regrese al xib y conecte esos IBOutlets
! En total, aquí está lo que debería ser la pestaña "Propietario del archivo":
Parece que hemos terminado ... pero espera! ¡No hicimos nada para poner en marcha un menú de "Abrir en ..."! Bueno, resulta que hay algunos problemas en el archivo .plist necesarios. Abra la aplicación .plist (con un clic derecho rápido, luego seleccione Abrir como> Código fuente) y pegue lo siguiente:
<?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>
[Nota al margen: ten cuidado con el código fuente de cualquier plist, si no sabes lo que estás haciendo, podrías obtener el temido error ''Este archivo ha sido dañado'' de Xcode]
Si hiciera clic derecho y selecciona Abrir como> Lista de propiedades, se vería así:
Hay otro campo MUY importante llamado "Aplicación compatible con el intercambio de archivos de iTunes". Debe configurarse en "SÍ", o su aplicación no se mostrará en iTunes como compatible con el uso compartido de archivos.
El campo ''Tipos de documento'' especifica los tipos de documentos que puede abrir nuestro ejemplo. Expanda la flecha para encontrar su función y UTI. Estos son identificadores únicos (identificadores de tipos únicos, parece obvio qué significa ese acrónimo ahora, ¿no?) Que tiene cada tipo de archivo. Las IU son las que permiten que el buscador reemplace una imagen de documento genérica con esa imagen localizada agradable del tipo de archivo (¡no me crean, cambie el nombre de una extensión de archivo sin importancia a .ouhbasdvluhb y trate de obtener una buena imagen!) Si quisiera abrir mi propio formato personalizado (digamos un archivo .code) luego pondría algo como com.CodaFi.code
(notación DNS inversa para aquellos sin clave) en el campo UTI y el nombre del tipo de documento sería ''documento CodaFi''. Handler Rank and Role debe ser sencillo ya que nuestro rango de handler es alternativo (porque no poseemos el archivo) y nuestro rol es viewer (porque no necesitamos nada más importante. Nuestro ejemplo es solo un visor y no un editor, así que lo dejamos como tal.
Para referencia futura, las UTI tienen esquemas oficiales de nombres declarados por el sistema cuando provienen de fuentes respetadas (Oracle, Microsoft, incluso la propia Apple) que se pueden encontrar en la Guía de referencia de identificador de tipo uniforme , pero se enumeran here por motivos de pedantería.
Ahora, ¡corramos ''er! El código debería compilarse sin errores, suponiendo que hayas copiado literalmente y que las justas conexiones xib estén correctas. Ahora cuando inicie su aplicación por primera vez, debería tener la opción de abrir un documento en iBooks. ¡Deseleccionarlo, la verdadera carne del código es abrir otros documentos! Inicie Safari y busque cualquier PDF que Safari pueda abrir rápidamente o abrir. Luego, en el menú "Abrir en ...", aparece nuestra aplicación. Pinchalo. Obtendrás la pequeña animación de switcheroo y aparecerá una alerta con la ubicación del archivo. Cuando lo descarta, UIWebView
habrá cargado el PDF. La aplicación de correo tiene una funcionalidad similar con archivos adjuntos. También puede llamar esos archivos PDF a su aplicación.
Eso es todo, todo está hecho. Disfruta y feliz codificación!