1. Code
  2. Mobile Development
  3. iOS Development

iOS sucintamente: gestión de activos

Scroll to top
This post is part of a series called iOS Succinctly.
iOS Succinctly - Multi-Scene Applications

Spanish (Español) translation by Juliana Carvajal (you can also view the original English article)

Ahora que tenemos una comprensión básica de la gestión de escenas de iOS, el próximo gran tema a abordar es cómo gestionar los activos multimedia en una aplicación. Las aplicaciones de iOS almacenan sus activos utilizando el mismo sistema de archivos jerárquico que cualquier otro sistema operativo moderno. Los archivos de texto, imagen, audio y video se organizan en carpetas y se accede a ellos utilizando rutas de archivo familiares como Documents/SomePicture.png.

En este capítulo, aprenderemos sobre la estructura de archivos estándar de una aplicación; cómo agregar recursos a un proyecto; y cómo localizar, cargar y guardar archivos. También hablaremos sobre los activos necesarios para todas las aplicaciones de iOS.

A lo largo del capítulo, hablaremos sobre archivos y carpetas, pero ten en cuenta que el sistema de archivos debe estar completamente oculto para los usuarios de iOS. En lugar de mostrar a los usuarios los archivos y carpetas detrás de una aplicación, iOS alienta a los desarrolladores a presentar el sistema de archivos como documentos orientados al usuario. Por ejemplo, en una aplicación de bocetos, los dibujos deben enumerarse con nombres de visualización semánticos y organizarse en cuadernos de bocetos o una estructura organizativa abstracta similar. Nunca debes mostrar las rutas de los archivos de usuario como sketchbook-1/your-drawing.svg.


Resumen conceptual

La zona de pruebas de la aplicación

El sistema de archivos de iOS se diseñó pensando en la seguridad. En lugar de permitir que una aplicación acceda a todo el sistema de archivos de un dispositivo, iOS le da a cada aplicación su propio sistema de archivos separado (una caja de arena). Esto significa que tu aplicación no tiene acceso a los archivos generados por otras aplicaciones. Cuando necesitas acceder a información que no es propiedad de tu aplicación (por ejemplo, la lista de contactos del usuario), la solicita a un mediador (por ejemplo, el marco de la libreta de direcciones) en lugar de acceder a los archivos directamente.

Una caja de arena es como un mini sistema de archivos dedicado exclusivamente al funcionamiento de tu aplicación. Todas las aplicaciones utilizan una estructura de archivos canónica que consta de cuatro directorios de nivel superior, cada uno de los cuales almacena un tipo específico de archivo.

  • AppName.app, el paquete de aplicaciones que contiene el archivo ejecutable de tu aplicación y todos sus elementos multimedia necesarios. Puedes leer desde esta carpeta, pero nunca debes escribir en ella. La siguiente sección analiza los paquetes con más detalle.
  • Documentos/, una carpeta para contenido generado por el usuario y otros archivos de datos críticos que tu aplicación no puede volver a crear. El contenido de este directorio está disponible a través de iCloud.
  • Biblioteca/, una carpeta para archivos de aplicaciones que no son utilizados por el usuario, pero que aún deben persistir entre lanzamientos.
  • tmp/, una carpeta para archivos temporales que se utilizan mientras se ejecuta la aplicación. Los archivos de esta carpeta no persisten necesariamente entre los inicios de la aplicación. iOS eliminará automáticamente los archivos temporales cuando sea necesario mientras tu aplicación no se esté ejecutando, pero debes eliminar manualmente los archivos temporales tan pronto como hayas terminado con ellos como práctica recomendada.

Cuando el usuario instala una aplicación, se crea una nueva caja de arena que contiene todas estas carpetas. Después de eso, tu aplicación puede crear dinámicamente subdirectorios arbitrarios en cualquiera de estas carpetas de nivel superior. También hay algunos subdirectorios predefinidos, como se describe en la siguiente lista.

  • Biblioteca/Soporte de aplicaciones/, una carpeta para archivos de soporte que se pueden volver a crear si es necesario. Esto incluye contenido descargado y generado. Debes utilizar el atributo extendido com.apple.MobileBackup para evitar que se realice una copia de seguridad de esta carpeta.
  • Biblioteca/Caché/, una carpeta para archivos de caché. Estos archivos se pueden eliminar sin previo aviso, por lo que tu aplicación debería poder volver a crearlos correctamente. Esta carpeta también es un lugar apropiado para almacenar contenido descargado.

Es importante colocar los archivos en la carpeta adecuada para asegurarte de que se respalden correctamente sin consumir una cantidad innecesaria de espacio en el dispositivo del usuario. iTunes realiza automáticamente una copia de seguridad de los archivos en las carpetas Documentos/ y Biblioteca/ (con la excepción de Biblioteca/Caché/). Ni el paquete de la aplicación ni la carpeta tmp/ deberían necesitar una copia de seguridad.

Paquetes

Una aplicación de iOS no es solo un ejecutable. También contiene medios, archivos de datos y posiblemente texto localizado para diferentes regiones. Para simplificar la implementación, Xcode envuelve el ejecutable y todos sus archivos requeridos en un tipo especial de carpeta llamada paquete de aplicación. A pesar de ser una carpeta, un paquete de aplicaciones usa la extensión .app. Puedes pensar en un paquete de aplicaciones como un archivo ZIP que ejecuta una aplicación cuando la abres.

Dado que tu paquete de aplicaciones contiene todos tus activos multimedia, deberás interactuar con él mientras se ejecuta el programa. La clase NSBundle facilita la búsqueda de archivos específicos en tu paquete de aplicaciones, que luego pueden cargar otras clases. Por ejemplo, puedes ubicar un archivo de imagen en particular usando NSBundle y luego agregarlo a una vista usando la clase UIImage. Haremos esto en otra sección, "El paquete de aplicaciones".


Creación de la aplicación de ejemplo

Este capítulo utiliza una aplicación sencilla para explorar algunos de los métodos fundamentales para acceder a archivos en iOS. Primero, abre Xcode, crea un nuevo proyecto y selecciona Aplicación de vista única para la plantilla.

tutorial_imageFigura 81: Creación de una nueva aplicación de vista única

Usa AssetManagement para el Nombre del producto, edu.self para el Identificador de la empresa y asegúrate de que Usar guiones gráficos y Usar recuento de referencias automático estén seleccionados.

tutorial_imagetutorial_imagetutorial_imageFigura 82: Configuración del nuevo proyecto

Puedes guardar el proyecto donde quieras.


El sistema de archivos

Antes de entrar en los recursos multimedia de una aplicación, veremos las herramientas básicas para acceder al sistema de archivos. Las próximas secciones discuten cómo generar rutas de archivo, crear archivos de texto sin formato y volver a cargarlos en la aplicación.

Ubicación de directorios estándar

Una de las tareas más comunes del sistema de archivos es generar la ruta a un recurso en particular. Pero, antes de que puedas obtener acceso a un archivo determinado, debes encontrar la ruta al espacio aislado de la aplicación o una de las carpetas de nivel superior que se discutieron anteriormente. La forma más sencilla de hacerlo es a través de la función global NSHomeDirectory(), que devuelve la ruta absoluta a la zona de pruebas de la aplicación. Por ejemplo, intenta cambiar el método viewDidLoad de ViewController.m a lo siguiente:

1
- (void)viewDidLoad {
2
    [super viewDidLoad];
3
    NSString *sandboxPath = NSHomeDirectory();
4
    NSLog(@"The app sandbox resides at: %@", sandboxPath);
5
}

Cuando la aplicación se carga en el simulador de iOS, deberías ver algo como lo siguiente en el panel de salida de Xcode.

1
/Users/ryan/Library/Application Support/iPhone Simulator/6.0/Applications/9E38D1C4-8B11-4599-88BE-CD9E36C21A41

Esta ruta representa la raíz de tu aplicación. Si navegas a este directorio en una terminal, encontrarás los siguientes cuatro directorios.

1
AssetManagement.app
2
Documents/
3
Library/
4
tmp/

No es sorprendente que esta sea la estructura de archivos canónica discutida anteriormente. Por supuesto, NSHomeDirectory() devolverá una ruta diferente cuando tu aplicación se esté ejecutando en un dispositivo iOS. La idea detrás de usar NSHomeDirectory() en lugar de generar manualmente la ruta a tu aplicación es asegurarte de tener siempre la ruta raíz correcta, independientemente de dónde resida tu aplicación.

La función relacionada NSTemporaryDirectory() devuelve la ruta al directorio tmp/. Para las otras carpetas de aplicaciones estándar, deberás usar la clase NSFileManager.

1
- (void)viewDidLoad {
2
    [super viewDidLoad];
3
    NSFileManager *sharedFM = [NSFileManager defaultManager];
4
    NSArray *paths = [sharedFM URLsForDirectory:NSLibraryDirectory
5
                                      inDomains:NSUserDomainMask];
6
    if ([paths count] > 0) {
7
        NSLog(@"The Library subfolder: %@", paths[0]);
8
    }
9
}

Como puedes ver, NSFileManager se implementa como singleton y se debe acceder a la instancia compartida a través del método de clase defaultManager. La enumeración NSSearchPathDirectory define varias constantes que representan las ubicaciones estándar utilizadas por las aplicaciones OS X e iOS. Algunas de estas ubicaciones (p. Ej., NSDesktopDirectory) no son aplicables en las aplicaciones de iOS; sin embargo, el método URLsForDirectory:inDomains: seguirá devolviendo la subcarpeta correspondiente en la zona de pruebas de la aplicación. Las constantes para los directorios que hemos discutido se enumeran a continuación.

1
NSDocumentDirectory                /*  Documents/                    */
2
NSLibraryDirectory                 /*  Library/                      */
3
NSCachesDirectory                  /*  Library/Caches                */
4
NSApplicationSupportDirectory      /*  Library/Application Support/  */

El método URLsForDirectory:inDomains: devuelve un NSArray que contiene objetos NSURL, que es una representación orientada a objetos de una ruta de archivo.

Generación de rutas de archivo

Una vez que tengas la ubicación de uno de los directorios estándar, puedes ensamblar manualmente la ruta a un archivo específico usando los métodos de instancia NSURL. Ten en cuenta que NSString también proporciona utilidades relacionadas, pero NSURL es la forma preferida de representar rutas de archivo.

Por ejemplo, el método URLByAppendingPathComponent: proporciona una forma sencilla de generar la ruta a un archivo específico. El siguiente fragmento crea una ruta a un archivo llamado someData.txt en el directorio/biblioteca de la aplicación.

1
- (void)viewDidLoad {
2
    [super viewDidLoad];
3
    NSFileManager *sharedFM = [NSFileManager defaultManager];
4
    NSArray *paths = [sharedFM URLsForDirectory:NSLibraryDirectory
5
                                      inDomains:NSUserDomainMask];
6
    if ([paths count] > 0) {
7
        NSURL *libraryPath = paths[0];
8
        NSURL *appDataPath = [libraryPath 
9
                              URLByAppendingPathComponent:@"someData.txt"];
10
        NSLog(@"%@", appDataPath);
11
    }
12
}

Algunos de los otros métodos útiles de instancia de NSURL se describen en la siguiente lista. Juntos, proporcionan la funcionalidad básica para navegar por una jerarquía de archivos y determinar manualmente los nombres y tipos de archivos.

  • URLByDeletingLastPathComponent devuelve una nueva NSURL que representa la carpeta principal de la ruta de recepción.
  • lastPathComponent devuelve el componente final en la ruta como una cadena. Puede ser un nombre de carpeta o un nombre de archivo, según la ruta.
  • pathExtension devuelve la extensión de archivo de la ruta como una cadena. Si la ruta no contiene un punto, devuelve una cadena vacía; de lo contrario, devuelve el último grupo de caracteres que sigue a un punto.
  • pathComponents descompone la ruta en sus partes componentes y las devuelve como un NSArray.

Guarda y carga archivos

Es importante comprender que NSURL solo describe la ubicación de un recurso. No representa el archivo o la carpeta en sí. Para obtener los datos del archivo, necesitas alguna forma de interpretarlos. La clase NSData proporciona una API de bajo nivel para leer en bytes sin procesar, pero la mayoría de las veces querrás utilizar una interfaz de nivel superior para interpretar el contenido de un archivo.

Los marcos de iOS incluyen muchas clases para guardar y cargar diferentes tipos de archivos. Por ejemplo, NSString puede leer y escribir archivos de texto, UIImage puede mostrar imágenes dentro de una vista y AVAudioPlayer puede reproducir música cargada desde un archivo. Veremos UIImage una vez que lleguemos al paquete de la aplicación, pero por ahora, sigamos con la manipulación básica de archivos de texto.

Para guardar un archivo con NSString, usa el método: writeToURL:automáticamente:codificación:error. El primer argumento es una NSURL que representa la ruta del archivo, el segundo determina si se debe guardar o no en un archivo auxiliar primero, y el tercero es una de las constantes definidas por la enumeración NSStringEncoding, y el argumento de error es una referencia a un NSError instancia que registrará los detalles del error si el método falla. El siguiente fragmento demuestra  writeToURL:automáticamente:codificación:error: al crear un archivo de texto sin formato llamado someData.txt en la carpeta Biblioteca/.

1
- (void)viewDidLoad {
2
    [super viewDidLoad];
3
    NSFileManager *sharedFM = [NSFileManager defaultManager];
4
    NSArray *paths = [sharedFM URLsForDirectory:NSLibraryDirectory
5
                                      inDomains:NSUserDomainMask];
6
    if ([paths count] > 0) {
7
        NSURL *libraryPath = paths[0];
8
        NSURL *appDataPath = [libraryPath  
9
                              URLByAppendingPathComponent:@"someData.txt"];
10
        
11
        NSString *someAppData = @"Hello, World! This is a file I created dynamically";
12
        NSError *error = nil;
13
        BOOL success = [someAppData writeToURL:appDataPath
14
                                     atomically:YES
15
                                       encoding:NSUnicodeStringEncoding
16
                                          error:&error];
17
        if (success) {
18
            NSLog(@"Wrote some data to %@", appDataPath);
19
        } else {
20
            NSLog(@"Could not write data to file. Error: %@", error);
21
        }
22
    }
23
}

Al guardar o cargar archivos de texto, es imperativo especificar la codificación del archivo; de lo contrario, tus datos de texto podrían alterarse inesperadamente al escribir o leer un archivo. Aquí se incluyen algunas de las constantes de codificación comunes.

1
NSASCIIStringEncoding7-bit ASCII encoding with 8-bit chars (ASCII values 0-127).
2
NSISOLatin1StringEncoding8-bit ISO Latin 1 encoding.
3
NSUnicodeStringEncodingUnicode encoding.

En caso de duda, probablemente desees utilizar NSUnicodeStringEncoding para asegurarte de que los caracteres de varios bytes se interpreten correctamente.

Para cargar un archivo de texto, puedes usar el stringWithContentsOfURL:encoding:error:relacionado. Esto funciona de manera muy similar a writeToURL:automatically:encoding:error:, pero se implementa como un método de clase en lugar de un método de instancia. El siguiente ejemplo carga el archivo de texto creado por el fragmento anterior de nuevo en la aplicación.

1
- (void)viewDidLoad {
2
    [super viewDidLoad];
3
    NSFileManager *sharedFM = [NSFileManager defaultManager];
4
    NSArray *paths = [sharedFM URLsForDirectory:NSLibraryDirectory
5
                                      inDomains:NSUserDomainMask];
6
    if ([paths count] > 0) {
7
        NSURL *libraryPath = paths[0];
8
        NSURL *appDataPath = [libraryPath 
9
                              URLByAppendingPathComponent:@"someData.txt"];
10
        
11
        NSError *error = nil;
12
        NSString *loadedText = [NSString 
13
                                stringWithContentsOfURL:appDataPath
14
                                encoding:NSUnicodeStringEncoding
15
                                error:&error];
16
        if (loadedText != nil) {
17
            NSLog(@"Successfully loaded text: %@", loadedText);
18
        } else {
19
            NSLog(@"Could not load data from file. Error: %@", error);
20
        }
21
    }
22
}

En el mundo real, probablemente guardarás y cargarás datos que se generaron dinámicamente en lugar de codificarlos como una cadena literal. Por ejemplo, puedes almacenar las preferencias de la plantilla o la información del usuario que debe persistir entre los lanzamientos de aplicaciones en un archivo de texto. Es completamente posible cargar e interpretar manualmente estos datos a partir de texto sin formato, pero ten en cuenta que hay varias herramientas integradas para trabajar y almacenar datos estructurados o incluso objetos Objective-C completos. Por ejemplo, NSDictionary define un método llamado dictionaryWithContentsOfURL: que carga un archivo XML que contiene pares clave-valor.

Manipular directorios

Los métodos NSString descritos anteriormente combinan la creación de un archivo y la escritura de contenido en un solo paso, pero para crear directorios, necesitamos volver a la clase NSFileManager. Define varios métodos que te permiten alterar el contenido de un directorio.

Creando Directorios

El método createDirectoryAtURL:withIntermediateDirectories:atributos:error: instancia crea un nuevo directorio en la ruta especificada. El segundo argumento es un valor booleano que determina si los directorios intermedios deben crearse automáticamente o no, los atributos te permiten definir atributos de archivo para el nuevo directorio y el argumento final es una referencia a una instancia de NSError que contendrá los detalles del error en caso de que el método falle.

Por ejemplo, si tu aplicación utiliza plantillas personalizadas descargadas de un servidor, puedes guardarlas en Biblioteca/Plantillas/. Para crear esta carpeta, puedes usar algo como lo siguiente.

1
- (void)viewDidLoad {
2
    [super viewDidLoad];
3
    NSFileManager *sharedFM = [NSFileManager defaultManager];
4
    NSArray *paths = [sharedFM URLsForDirectory:NSLibraryDirectory
5
                                      inDomains:NSUserDomainMask];
6
    if ([paths count] > 0) {
7
        NSURL *libraryPath = paths[0];
8
        NSURL *templatesPath = [libraryPath 
9
                                URLByAppendingPathComponent:@"Templates"];
10
        
11
        NSError *error = nil;
12
        BOOL success = [sharedFM createDirectoryAtURL:templatesPath
13
                          withIntermediateDirectories:YES
14
                                           attributes:nil
15
                                                error:&error];
16
        if (success) {
17
            NSLog(@"Successfully created a directory at %@", 
18
                  templatesPath);
19
        } else {
20
            NSLog(@"Could not create the directory. Error: %@", error);
21
        }
22
    }
23
}

Dejar el argumento de atributos como nulo le indica al método que use el grupo, el propietario y los permisos predeterminados para el proceso actual.

Mover archivos/directorios

La clase NSFileManager también se puede utilizar para mover o cambiar el nombre de archivos y carpetas a través de su método de instancia moveItemAtURL:toURL:error:. Funciona de la siguiente manera.

1
- (void)viewDidLoad {
2
    [super viewDidLoad];
3
    NSFileManager *sharedFM = [NSFileManager defaultManager];
4
    NSArray *paths = [sharedFM URLsForDirectory:NSLibraryDirectory
5
                                      inDomains:NSUserDomainMask];
6
    if ([paths count] > 0) {
7
        NSURL *libraryPath = paths[0];
8
        NSURL *sourcePath = [libraryPath 
9
                             URLByAppendingPathComponent:@"someData.txt"];
10
        NSURL *destinationPath = [libraryPath 
11
                        URLByAppendingPathComponent:@"someOtherData.txt"];
12
        
13
        NSError *error = nil;
14
        BOOL success = [sharedFM moveItemAtURL:sourcePath
15
                                         toURL:destinationPath
16
                                         error:&error];
17
        if (success) {
18
            NSLog(@"Successfully moved %@ to %@",
19
                  sourcePath,
20
                  destinationPath);
21
        } else {
22
            NSLog(@"Could not move the file. Error: %@", error);
23
        }
24
    }
25
}

Esto cambia el nombre del archivo someData.txt que creamos anteriormente a someOtherData.txt. Si necesitas copiar archivos, puedes usar copyItemAtURL:toURL:error:, que funciona de la misma manera, pero deja intacto el archivo fuente.

Eliminar archivos/directorios

Finalmente, el método removeItemAtURL:error: de NSFileManager te permite eliminar archivos o carpetas. Simplemente pasa la instancia de NSURL que contiene la ruta que deseas eliminar, de la siguiente manera.

1
[sharedFM removeItemAtURL:targetURL error:&error];

Si targetURL es un directorio, tu contenido se eliminará de forma recursiva.

Listado del contenido de un directorio

También es posible enumerar los archivos y subdirectorios de una carpeta mediante el método enumeratorAtPath: de NSFileManager. Esto devuelve un objeto NSDirectoryEnumerator que puedes usar para recorrer cada archivo o carpeta. Por ejemplo, puedes enumerar el contenido del directorio Documentos/ de nivel superior de la siguiente manera:

1
- (void)viewDidLoad {
2
    [super viewDidLoad];
3
    NSFileManager *sharedFM = [NSFileManager defaultManager];
4
    NSArray *paths = [sharedFM URLsForDirectory:NSDocumentDirectory
5
                                      inDomains:NSUserDomainMask];
6
    if ([paths count] > 0) {
7
        NSString *documentsPath = [paths[0] path];
8
        NSLog(@"%@", documentsPath);
9
        NSDirectoryEnumerator *enumerator = [sharedFM enumeratorAtPath:documentsPath];
10
        id object;
11
        while(object = [enumerator nextObject]) {
12
            NSLog(@"%@", object);
13
        }
14
    }
15
}

Ten en cuenta que este enumerador recorrerá todos los subdirectorios. Puedes modificar este comportamiento llamando al método skipDescendents en la instancia  NSDirectoryEnumerator, o utilizando el método enumeratorAtURL:includedPropertiesForKeys:options:errorHandler: de NSFileManager más sofisticado.


El paquete de aplicaciones

Los métodos de acceso a archivos discutidos anteriormente generalmente solo se usan para interactuar con archivos que se crean dinámicamente en tiempo de ejecución. Las carpetas estándar Biblioteca/, Documentos/ y tmp/ son locales en el dispositivo en el que está instalada la aplicación e, inicialmente, están vacías. Para los activos que son una parte esencial de la aplicación en sí (en lugar de ser creados por la aplicación), necesitamos otra herramienta: un paquete de aplicaciones.

El paquete de la aplicación representa tu proyecto completo, que está compuesto por el ejecutable de iOS, junto con todas las imágenes, sonidos, videos y archivos de configuración requeridos por ese ejecutable. El paquete de aplicaciones es lo que realmente se instala cuando un usuario descarga tu aplicación, por lo que, a diferencia del contenido de los directorios de la zona de pruebas, puedes asumir que todos los recursos de apoyo estarán presentes independientemente del dispositivo en el que resida tu aplicación.

Conceptualmente, un paquete es solo un grupo de archivos, pero interactuar con él es un poco diferente a usar los métodos del sistema de archivos de la sección anterior. En lugar de generar manualmente rutas de archivo y guardar o cargar datos con métodos como writeToURL:automáticamente:codificación:error:, utiliza la clase NSBundle como una interfaz de alto nivel para los recursos multimedia de tu aplicación. Esto delega algunos detalles esenciales detrás de los activos de los medios al sistema subyacente.

Además de los recursos multimedia personalizados, el paquete de aplicaciones también contiene varios recursos necesarios, como el icono de la aplicación que aparece en la pantalla de inicio del usuario y archivos de configuración importantes. Hablaremos de estos en la sección "Recursos necesarios".

Agrega activos al paquete

Recuerda que los activos del paquete de aplicaciones son estáticos, por lo que siempre se incluirán en el momento de la compilación. Para agregar un recurso multimedia a tu proyecto, simplemente arrastra el(los) archivo(s) desde el Finder al panel Navegador de proyectos en Xcode. Vamos a agregar un archivo llamado syncfusion-logo.jpg, que puedes encontrar en el paquete de recursos de este libro, pero puedes usar cualquier imagen que desees. En la siguiente sección, aprenderemos cómo acceder al paquete para mostrar esta imagen en la vista.

tutorial_imagetutorial_imagetutorial_imageFigura 83: Agrega una imagen al paquete de la aplicación

Después de soltar el ratón, Xcode te presentará un cuadro de diálogo solicitando opciones de configuración. Se debe seleccionar la casilla de verificación Copiar elementos en la carpeta del grupo de destino. Esto le dice a Xcode que copie los activos en la carpeta del proyecto, lo que suele ser un comportamiento deseable. La opción Crear grupos para cualquier carpeta agregada utiliza el mecanismo de agrupación de Xcode. Agregar a destinos es la opción de configuración más importante. Define con qué objetivos de compilación se compilará el activo. Si no se seleccionó AssetManagement, sería como si nunca lo hubiéramos agregado al proyecto.

tutorial_imagetutorial_imagetutorial_imageFigura 84: Selección de opciones de configuración para los nuevos recursos multimedia

Después de hacer clic en Finalizar, deberías ver tu archivo en el Navegador de proyectos de Xcode:

tutorial_imageFigura 85: El recurso multimedia en el Navegador de proyectos

Ahora que tienes un recurso personalizado en tu paquete de aplicaciones, puedes acceder a él a través de NSBundle.

Accede a los recursos incluidos

En lugar de crear manualmente rutas de archivo con NSURL, siempre debes usar la clase NSBundle como interfaz programática para tu paquete de aplicaciones. Proporciona una funcionalidad de búsqueda optimizada y capacidades de internacionalización integradas, de las que hablaremos en el próximo capítulo.

El método de la clase mainBundle devuelve la instancia de NSBundle que representa tu paquete de aplicación. Una vez que tengas eso, puedes ubicar recursos usando el método de instancia pathForResource: ofType:. iOS utiliza convenciones especiales de nomenclatura de archivos que hacen posible que NSBundle devuelva diferentes archivos dependiendo de cómo se vaya a utilizar el recurso. Separar el nombre del archivo de la extensión permite que pathForResource:ofType: averigüe qué archivo usar automáticamente, y permite que NSBundle seleccione automáticamente archivos localizados.

Por ejemplo, el siguiente código ubica un archivo JPEG llamado syncfusion-logo en el paquete de la aplicación:

1
- (void)viewDidLoad {
2
    [super viewDidLoad];
3
    
4
    // Find the resource.

5
    NSString *imagePath = [[NSBundle mainBundle]
6
                           pathForResource:@"syncfusion-logo"
7
                           ofType:@"jpg"];
8
    NSLog(@"Image path: %@", imagePath);
9
    
10
    // Load the resource.

11
    UIImage *imageData = [[UIImage alloc] 
12
                          initWithContentsOfFile:imagePath];
13
    if (imageData != nil) {
14
        NSLog(@"Image size: %.0fx%.0f",
15
              imageData.size.width, imageData.size.height);
16
    } else {
17
        NSLog(@"Could not load the file");
18
    }
19
}

Al igual que los archivos de texto, localizar una imagen y cargarla son acciones independientes. La primera llamada a NSLog() debería mostrar algo como /path/to/sandbox/AssetManagement.app/your-image.jpg en el Panel de salida de Xcode.

La clase UIImage representa el contenido de un archivo de imagen y funciona con prácticamente cualquier tipo de formato de imagen (JPEG, PNG, GIF, TIFF, BMP y algunos otros). Una vez que hayas obtenido la ubicación de la imagen con NSBundle, puedes pasarla al método initWithContentsOfFile: de UIImage. Si el archivo se cargó correctamente, podrás acceder a las dimensiones de la imagen a través de la propiedad de tamaño, que es una estructura CGSize que contiene los campos de punto flotante de ancho y alto.

Si bien UIImage define algunos métodos para dibujar la imagen asociada en la pantalla (a saber, drawAtPoint: y drawInRect:), a menudo es más fácil mostrarlo usando la clase UIImageView. Dado que es una subclase de UIView, se puede agregar a la jerarquía de vistas existente mediante el método addSubview: común a todas las instancias de vista. UIImageView también proporciona una interfaz conveniente para controlar la reproducción de la animación. Para mostrar UIImage en el objeto de vista raíz, cambia el método viewDidLoad de ViewController.m a lo siguiente.

1
- (void)viewDidLoad {
2
    [super viewDidLoad];
3
    
4
    // Find the image.

5
    NSString *imagePath = [[NSBundle mainBundle]
6
                           pathForResource:@"syncfusion-logo"
7
                           ofType:@"jpg"];
8
    
9
    // Load the image.

10
    UIImage *imageData = [[UIImage alloc] 
11
                          initWithContentsOfFile:imagePath];
12
    if (imageData != nil) {
13
        // Display the image.

14
        UIImageView *imageView = [[UIImageView alloc] 
15
                                  initWithImage:imageData];
16
        [[self view] addSubview:imageView];
17
    } else {
18
        NSLog(@"Could not load the file");
19
    }
20
}

Cuando compiles el proyecto, deberías ver tu imagen en la esquina superior izquierda de la pantalla. De forma predeterminada, la imagen no se escalará. Por lo tanto, si tu imagen es más grande que la pantalla, se recortará en consecuencia.

tutorial_imagetutorial_imagetutorial_imageFigura 86: Un objeto UIImageView recortado

Para cambiar la forma en que tu UIImageView escala tu contenido, debes usar la propiedad contentMode de UIView. Toma un valor de tipo UIViewContentMode, que es una enumeración que define los siguientes comportamientos.

  • UIViewContentModeScaleToFill, escala para llenar las dimensiones de la vista, cambiando la relación de aspecto de la imagen si es necesario.
  • UIViewContentModeScaleAspectFit, escala para adaptarte a las dimensiones de la vista, manteniendo la relación de aspecto de la imagen.
  • UIViewContentModeScaleAspectFill, escala para llenar las dimensiones de la vista, manteniendo la relación de aspecto de la imagen. Esto puede hacer que se recorte parte de la imagen.
  • UIViewContentModeCenter, usa el tamaño de la imagen original, pero céntralo horizontal y verticalmente.
  • UIViewContentModeTop, usa el tamaño de la imagen original, pero céntralo horizontalmente y alinéalo con la parte superior de la vista.
  • UIViewContentModeBottom, usa el tamaño de la imagen original, pero céntralo horizontalmente y alinéalo con la parte inferior de la vista.
  • UIViewContentModeLeft, usa el tamaño de la imagen original, pero céntrala verticalmente y alinéala a la izquierda de la vista.
  • UIViewContentModeRight, usa el tamaño de la imagen original, pero céntralo verticalmente y alinéalo a la derecha de la vista.
  • UIViewContentModeTopLeft, usa el tamaño de la imagen original, pero alinéalo con la esquina superior izquierda de la vista.
  • UIViewContentModeTopRight, usa el tamaño de la imagen original, pero alinéalo con la esquina superior derecha de la vista.
  • UIViewContentModeBottomLeft, usa el tamaño de la imagen original, pero alinéalo con la esquina inferior izquierda de la vista.
  • UIViewContentModeBottomRight, usa el tamaño de la imagen original, pero alinéalo con la esquina inferior derecha de la vista.

Por ejemplo, si deseas que tu imagen se reduzca para ajustarse al ancho de la pantalla mientras mantienes su relación de aspecto, usarías UIViewContentModeScaleAspectFit para contentMode y luego cambiarías el ancho de las dimensiones de la vista de la imagen para que coincida con el ancho de la pantalla (disponible a través del objeto [UIScreen mainScreen]). Agrega lo siguiente al método viewDidLoad del ejemplo anterior después de [[self view] addSubview: imageView]; línea.

1
CGRect screenBounds = [[UIScreen mainScreen] bounds];
2
imageView.contentMode = UIViewContentModeScaleAspectFit;
3
CGRect frame = imageView.frame;
4
frame.size.width = screenBounds.size.width;
5
imageView.frame = frame;

La propiedad de marco de todas las instancias de UIView define la posición y el área visible de la vista (es decir, sus dimensiones). Después de cambiar el ancho del marco, el comportamiento UIViewContentModeScaleAspectFit calculó automáticamente la altura del marco, dando como resultado la siguiente imagen.

tutorial_imagetutorial_imagetutorial_imageFigura 87: Reduce una imagen para que se ajuste al ancho de la pantalla

Si bien esta sección extrajo una imagen del paquete, recuerda que es igual de fácil acceder a otros tipos de medios. En la sección anterior, vimos cómo se pueden cargar archivos de texto con NSString; esto funciona de la misma manera con los paquetes. Un video funciona de manera similar a una imagen en el sentido de que iOS proporciona una clase dedicada (MPMoviePlayerController) para incorporarlo en una jerarquía de vista existente. Los archivos de audio son ligeramente diferentes, ya que la reproducción no está necesariamente vinculada a una vista dedicada. Analizaremos las capacidades de audio de iOS más adelante en este libro. Por ahora, volvamos al paquete de aplicaciones.

Recursos necesarios

Además de cualquier recurso multimedia personalizado que tu aplicación pueda necesitar, también hay tres archivos que deben estar en el paquete de tu aplicación. Estos se describen a continuación.

  • Lista de propiedades de información, el archivo de configuración que define opciones críticas como las capacidades requeridas del dispositivo, orientaciones admitidas, etc.
  • Icono de la aplicación, el icono que aparece en la pantalla de inicio del usuario. Esto es lo que el usuario toca para iniciar tu aplicación.
  • Lanza imagen. Una vez que el usuario inicia tu aplicación, esta es la imagen que aparece brevemente mientras se carga.

Lista de propiedades de información

Una lista de propiedades (también conocida como “plist”) es un formato conveniente para almacenar datos estructurados. Facilita guardar matrices, diccionarios, fechas, cadenas y números en un archivo persistente y volver a cargarlo en una aplicación en tiempo de ejecución. Si alguna vez has trabajado con datos JSON, es la misma idea.

La lista de propiedades de información es un tipo especial de lista de propiedades almacenada en un archivo llamado Info.plist en tu paquete de aplicaciones. Puedes considerarlo como un NSDictionary basado en archivos cuyos pares clave-valor definen las opciones de configuración de tu aplicación. El Info.plist para nuestra aplicación de ejemplo se genera a partir de un archivo llamado AssetManagement-Info.plist, y puedes editarlo directamente en Xcode seleccionándolo de la carpeta Archivos de apoyo en el Navegador de proyectos. Cuando lo abras, deberías ver algo como lo siguiente.

tutorial_imagetutorial_imagetutorial_imageFigura 88: Abre el archivo Info.plist en Xcode

Estas son todas las opciones de configuración proporcionadas por la plantilla. La columna más a la izquierda contiene el nombre de la opción y la más a la derecha contiene su valor. Ten en cuenta que los valores pueden ser booleanos, números, cadenas, matrices o incluso diccionarios completos.

De forma predeterminada, las claves se muestran con títulos legibles por humanos, pero ayuda, especialmente cuando se aprende iOS por primera vez, ver las claves sin formato a las que hace referencia la documentación oficial. Para mostrar las claves sin formato, presiona Ctrl+clic en cualquier lugar del editor Info.plist y selecciona Mostrar claves/valores sin formato. Estas son las cadenas que debes utilizar cuando desees acceder a las opciones de configuración mediante programación (como se explica en la siguiente sección).

tutorial_imagetutorial_imagetutorial_imageFigura 89: Visualización de claves sin formato

La columna más a la izquierda ahora debería mostrar claves como CFBundleDevelopmentRegion, CFBundleDisplayName, etc. Todos los archivos Info.plist requieren las siguientes claves.

  • UIRequiredDeviceCapabilities es una matriz que contiene los requisitos del dispositivo para tu aplicación. Esta es una forma en que Apple determina qué usuarios pueden ver tu aplicación en la App Store. Los valores posibles se enumeran en la sección UIRequiredDeviceCapabilities de la Referencia de claves de iOS.
  • UISupportedInterfaceOrientations es una matriz que define las orientaciones que admite tu aplicación. Los valores aceptables incluyen UIInterfaceOrientationPortrait, UIInterfaceOrientationLandscapeLeft, UIInterfaceOrientationLandscapeRight y UIInterfaceOrientationPortraitUpsideDown.
  • CFBundleIconFile es una matriz que contiene los nombres de archivo de todos los iconos de tu aplicación. Hablaremos más sobre los iconos de aplicaciones en un momento.

La plantilla proporciona valores predeterminados para los requisitos del dispositivo y las orientaciones admitidas, como se muestra en la Figura 90.

tutorial_imagetutorial_imagetutorial_imageFigura 90: Valores predeterminados para los requisitos del dispositivo y las orientaciones admitidas

Acceso a las opciones de configuración

La mayoría de las opciones de configuración definidas en Info.plist son utilizadas internamente por iOS, pero es posible que ocasionalmente necesites acceder a ellas manualmente. Puedes obtener una representación NSDictionary de Info.plist a través del método infoDictionary de NSBundle. Por ejemplo, si deseas realizar un comportamiento de inicio personalizado basado en una clave en Info.plist, puedes hacer una verificación rápida en la aplicación: didFinishLaunchingWithOptions: método de AppDelegate.m, así:

1
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
2
    NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary];
3
    NSArray *supportedOrientations = [infoDict objectForKey:@"UISupportedInterfaceOrientations"];
4
    if ([supportedOrientations containsObject:@"UIInterfaceOrientationPortraitUpsideDown"]) {
5
        NSLog(@"Do something special to enable an upside-down app");
6
    } else {
7
        NSLog(@"Can assume the app won't display upside-down");
8
    }
9
    return YES;
10
}

Ícono(s) de la aplicación

Tu paquete de aplicaciones debe incluir al menos un icono para mostrar en la pantalla de inicio, pero también es posible especificar varios iconos para diferentes situaciones. Por ejemplo, es posible que desees utilizar un diseño diferente para el icono más pequeño que aparece en los resultados de búsqueda o utilizar una imagen de alta resolución para dispositivos con pantallas Retina. Xcode se encarga de todo esto por ti.

La clave CFBundleIconFiles en Info.plist debe ser una matriz que contenga los nombres de archivo de todos los iconos de tu aplicación. Con la excepción del icono de la App Store, puedes usar el nombre que desees para los archivos. Ten en cuenta que no es necesario que especifiques el uso previsto de cada archivo; iOS seleccionará el icono apropiado automáticamente en función de las dimensiones.

tutorial_imagetutorial_imagetutorial_imageFigura 91: Iconos de aplicaciones de iPhone personalizados con versiones de alta resolución para pantallas Retina

Si tu aplicación es compatible con dispositivos con pantallas Retina, también debes incluir una versión de alta resolución de cada icono y asignarle el mismo nombre de archivo con @2x adjunto. Por ejemplo, si tu archivo de icono principal se llamaba app-icon.png, la versión de pantalla Retina debería llamarse app-icon@2x.png.

La siguiente tabla enumera los iconos estándar de las aplicaciones de iOS, pero asegúrate de visitar las Pautas de la interfaz humana de iOS para obtener una discusión más detallada. Este documento también proporciona amplias pautas para la creación de gráficos de iconos. Todos los iconos deben usar el formato PNG.

Tipo de icono Plataforma Requerido Tamaño estándar Tamaño de la retina Descripción/
Icono de la tienda de aplicaciones iPhone y iPad Si 512 × 512 1024 × 1024 La imagen presentada a los clientes en iTunes. Este archivo debe llamarse iTunesArtwork o iTunesArtwork@2x (sin extensión).
Icono principal iPhone Si 57 × 57 114 × 114 El icono que aparece en la pantalla de inicio del iPhone.
Icono principal iPad Si 72 × 72 144 × 144 El icono que aparece en la pantalla de inicio del iPad.
Pequeño icono iPhone No 29 × 29 58 × 58 El icono que se muestra junto a los resultados de la búsqueda y en la aplicación Configuración para iPhone.
Pequeño icono iPad No 50 × 50 100 × 100 El icono que se muestra junto a los resultados de la búsqueda y en la aplicación Configuración para iPad.

A continuación, agregarás un icono a la aplicación de ejemplo. En el paquete de recursos de este libro, encontrarás cuatro iconos de muestra llamados app-icon.png, app-icon@2x.png, app-icon-small.png y app-icon-small@2x.png. Arrastra todos estos al Navegador de proyectos para agregarlos a tu paquete de aplicaciones, y luego abre AssetManagement-Info.plist y asegúrate de que estás viendo claves o valores sin procesar. Agrega una nueva fila y escribe CFBundleIconFiles para la clave (no debes confundirte con CFBundleIconsCFBundleIconFile). Esto agregará automáticamente un elemento vacío a la matriz, que puedes ver haciendo clic en el triángulo junto al elemento CFBundleIconFiles. Agrega los cuatro archivos de iconos a la matriz, para que tengas el siguiente aspecto. El orden no importa.

tutorial_imagetutorial_imagetutorial_imageFigura 92: Agrega archivos de iconos a Info.plist

Ahora, cuando ejecutes tu proyecto, deberías ver un icono de aplicación personalizado en la pantalla de inicio. Es posible que debas reiniciar el simulador de iOS para que esto funcione.

tutorial_imagetutorial_imagetutorial_imageFigura 93: El icono de la aplicación personalizada en la pantalla de inicio

Intenta arrastrar la pantalla de inicio hacia la derecha varias veces hasta llegar a la pantalla de búsqueda. Si comienzas a escribir "gestión de activos", deberías ver la versión pequeña del icono en los resultados de la búsqueda, como se muestra en la Figura 94:

tutorial_imageFigura 94: El pequeño icono de la aplicación que se utiliza en los resultados de búsqueda

Y eso es todo lo que hay que hacer para personalizar los iconos de la aplicación de tu iPhone.

Inicia imagen(s)

El elemento multimedia requerido final para cualquier aplicación de iOS es una imagen de lanzamiento. Se muestra una imagen de inicio inmediatamente cuando el usuario abre tu aplicación. La idea es darle al usuario la impresión de que tu aplicación se inició inmediatamente, aunque puede tardar unos segundos en cargarse. Apple desaconseja a los desarrolladores que utilicen imágenes de lanzamiento como una página de información o una pantalla de presentación. En cambio, debería ser un esqueleto de la pantalla inicial de tu aplicación.

Por ejemplo, considera la aplicación maestro-detalle que construimos en el capítulo anterior. La imagen de lanzamiento ideal sería simplemente una lista maestra vacía:

tutorial_imagetutorial_imagetutorial_imageFigura 95: Imagen de inicio adecuada para una aplicación de detalles maestros

Como puedes ver en los capítulos anteriores, una imagen de inicio es esencialmente una captura de pantalla de la pantalla inicial de tu aplicación, menos cualquier dato dinámico. Esto evita cualquier cambio abrupto en la interfaz de usuario. Nuevamente, la idea es restar importancia al inicio de la aplicación haciendo que la transición sea lo más fluida posible desde la selección de la aplicación hasta la imagen de inicio y la pantalla inicial. El simulador de iOS tiene una práctica herramienta de captura de pantalla para crear imágenes de lanzamiento. Una vez que tu aplicación esté lista, ejecútala en el simulador y navega hasta Editar> Copiar pantalla.

Al igual que los iconos de aplicaciones, es posible tener varias imágenes de inicio según el dispositivo o la resolución de la pantalla. Las imágenes de lanzamiento usan el mismo afijo @2x para imágenes de alta resolución, pero también es posible apuntar a dispositivos específicos agregando un modificador de uso inmediatamente después del nombre base. Por ejemplo, el iPhone 5 tiene diferentes dimensiones de pantalla que las generaciones anteriores y, por lo tanto, requiere su propia imagen de lanzamiento. Para decirle a iOS que use un archivo en particular para dispositivos iPhone 5, agregaría -568h a su nombre base, dándole algo como launch-image-568h@2x.png (ten en cuenta que el iPhone 5 tiene una pantalla Retina, por lo que el lanzamiento asociado imagen siempre tendrá @2x en su nombre de archivo).

La siguiente tabla enumera los requisitos de dimensión para las imágenes de lanzamiento de iOS.

Plataforma Tamaño estándar Tamaño de la retina
iPhone (hasta cuarta generación) 320 × 480 640 × 960 iPhone (quinta generación) 640 × 1136 640 × 1136 iPad 768 × 1004 1536 × 2008

Una vez que tengas tu imagen de inicio, agregarla a tu aplicación es muy similar a configurar los iconos de la aplicación. Primero, agrega los archivos al nivel superior de tu paquete de aplicaciones y luego agrega el nombre base a la clave UILaunchImageFile de tu Info.plist. Por ejemplo, si tus imágenes de lanzamiento se llamaran launch-image.png, launch-image@2x.png y launch-image-568h@2x.png, usarías launch-image como el valor de UILaunchImageFile.

Si no especificas un UILaunchImageFile, iOS utilizarás los archivos Default.png, Default@2x.png y Default-568h@2x.png. Estas imágenes de lanzamiento predeterminadas son proporcionadas por las plantillas de Xcode.


Resumen

Este capítulo cubrió muchas de las herramientas de administración de activos integradas en iOS. Hablamos sobre el sistema de archivos sandbox de una aplicación y cómo leer y escribir archivos dinámicos desde varios directorios predefinidos. También analizamos el paquete de aplicaciones, que contiene todos los recursos que se distribuirán con la aplicación. Además de los activos multimedia personalizados, el paquete de aplicaciones debe incluir una lista de propiedades de información, iconos de aplicaciones e imágenes de inicio.

Los paquetes son una forma conveniente de distribuir una aplicación, pero también brindan capacidades de internacionalización integradas. En el próximo capítulo, aprenderemos cómo mostrar diferentes archivos a diferentes usuarios según su configuración de idioma. Y, gracias a NSBundle, esto requerirá muy poco código adicional.

Esta lección representa un capítulo sucinto de iOS, un libro electrónico gratuito del equipo de Syncfusion.