iphone objective-c uiimage nsbundle

iphone - UIImage imageNamed requiere pathForResource?



objective-c nsbundle (4)

En absoluto ... es la respuesta a la pregunta original:

¿Qué tan necesario es buscar una ruta a una imagen usando el método NSBundle pathForResource al crear un UIImage usando imageNamed?

No mucho ... es cuán correcta es la respuesta aceptada de Zoul y la otra de Ranga. Para ser justos: son correctos si se está hablando de la estructura del directorio del paquete de aplicaciones, o para el (raro) caso en que la imagen está en una carpeta "azul" en Xcode (más sobre esto más adelante), pero no para la mayoría casos comunes

De todos modos, a la única respuesta verdadera.

Como de costumbre, encontré esta pregunta al tratar de encontrar la respuesta yo mismo. Nunca encontré la documentación u otras respuestas satisfactorias, así que decidí probar.

Los detalles de mi prueba están todos a continuación, pero permítanme resumir los resultados aquí. En resumen, al usar imageNamed: para cargar sus imágenes, depende de dónde las coloque:

  1. si sus imágenes están en la raíz de su proyecto, incluso si están organizadas en un grupo Xcode puramente lógico, entonces no, no necesita pensar en la ruta: solo el nombre de la imagen.

  2. Si sus imágenes están en un grupo que está adjunto a un directorio en su sistema de archivos, a través de "crear grupos para carpetas agregadas", entonces no necesita preocuparse por el nombre.

  3. si sus imágenes están en un grupo "azul", que está adjuntado a un directorio en su sistema de archivos a través de "crear referencias de carpeta para carpetas agregadas", entonces puede cargarlo con imageNamed: especificando una ruta relativa como se sugiere (¿por casualidad?) por la respuesta aceptada anteriormente.

  4. si usa la alternativa principal a imageNamed :, imageWithContentsOfFile :, realmente necesita la ruta completa al archivo, incluida la ruta del paquete, lo que significa que necesita saber cómo la estructura del navegador Xcode se traduce en rutas en la estructura del directorio del paquete.

Las otras diferencias importantes entre estos dos métodos:

  • imageNamed no requiere que especifique la extensión del tipo de archivo, por lo que simplemente "icono" no "icono.png", mientras que imageWithContentsOfFile requiere el nombre completo del archivo
  • este primer punto ayuda con la segunda característica: imageNamed cargará automáticamente la versión retina de una imagen, si hay una, agregando @ 2x a su nombre de archivo. Entonces, si pides un "icono", en una pantalla retina intentará cargar "[email protected]". imageWithContentsOfFile no
  • imageNamed almacena en caché la imagen, y ahí radica gran parte de la controversia que la rodea: si buscas SO o la web en general, encontrarás muchas publicaciones que te recomiendan evitarla porque no borra su caché correctamente. Esto, sin embargo, se arregló hace años, por lo que no tiene que preocuparse porque no borre su caché. Sin embargo , todavía tienes que preocuparte por el hecho de que se almacena en la memoria caché. Si sus imágenes son grandes y no se cargan con mucha frecuencia, conservará la memoria al cargarlas desde el archivo y no almacenarlas en la memoria caché. Esto no tiene nada que ver con fugas: incluso si no tiene fugas, todavía tiene memoria limitada en el dispositivo y no desea almacenar en caché innecesariamente. Es el intercambio clásico de caché: ¿qué es más importante en su situación? Rendimiento de la memoria o rendimiento de la CPU (tiempo).

Y así sucesivamente con mis pruebas.

Lo que hice fue crear una aplicación sencilla de UITableView con 3 archivos de iconos simples, que se muestran en las filas de la tabla utilizando diferentes métodos. Los iconos son diferentes en su ubicación en la estructura del proyecto Xcode . Tenga en cuenta el énfasis en Xcode. La clave para entender la respuesta a la pregunta original es que hay tres estructuras de directorios de proyectos totalmente diferentes en una aplicación de iOS: la que ves en el navegador Xcode, la que está en el sistema de archivos para el mismo proyecto que ves en Finder (haga clic con el botón derecho en cualquier elemento del navegador de Xcode y seleccione "mostrar en el buscador") y, el que rara vez ve, la estructura de directorios "agrupados" de la aplicación desplegada. También puede ver este último en Finder, al encontrar su aplicación en ~ / Library / Application Support / iPhone Simulator y al profundizar en el directorio .app. Te mostraré una foto mía en un minuto.

Entonces, en mi aplicación, arrastré los tres íconos de los archivos de imagen png a Xcode de diferentes maneras:

  1. icon1.png (un reloj), lo arrastré como un archivo a la raíz del proyecto Xcode, luego creé un nuevo grupo en Xcode y lo arrastré a eso. Este grupo no está representado por ningún directorio en el sistema de archivos: es un grupo Xcode puro. De ahí su nombre: "JustGroup"

  2. icon2.png (un ojo), originalmente puse mi sistema de archivos en el directorio llamado "RealDir", y arrastré este directorio completo a Xcode, y cuando me preguntaron, elegí la opción "Crear grupos para cualquier carpeta agregada". Esto significa que el grupo RealDir en Xcode está conectado a un directorio real llamado RealDir en el sistema de archivos (en mi directorio de proyectos) y que icon2.png está ahí.

  3. icon3.png (un objetivo), también tenía en un directorio separado, que también arrastré a Xcode. Solo que esta vez elegí la segunda opción de radio "Crear referencias de carpeta para cualquier carpeta agregada". Esto crea un grupo llamado "azul" en Xcode. Más sobre de lo que se trata todo más adelante. Llamé a este grupo (y directorio) "FolderReference"

Aquí hay una foto de la elección que Xcode le brinda:

Y así es como se ve la estructura de mi proyecto en Xcode:

Ahora, en mi aplicación, utilicé dos métodos para cargar cada uno de los iconos: UIImage imageNamed: y UIImage imageWithContentsOfFile. Creé un montón de filas en mi tabla con el título de cada celda que es el nombre del grupo que contiene el icono: JustGroup, RealDir o FolderReference, más el nombre del método utilizado: imageNamed vs fromFile (que estoy usando como abreviatura de imageWithContentsOfFile)

La etiqueta de detalle de la celda (el texto más débil debajo del título) muestra el archivo o la ruta que le di al método.

Para ser claros, en el caso de "fromFile", estoy agregando la ruta del paquete al nombre "relativo" que ve. Entonces, para "fromFile", estoy usando este código:

NSString *bundlePath = [[NSBundle mainBundle] bundlePath]; NSString *imagePath = [NSString stringWithFormat:@"%@/%@", bundlePath, filePath]; UIImage *image = [UIImage imageWithContentsOfFile:imagePath];

donde "filePath" es la ruta que ve en la etiqueta de detalle de la celda de la tabla. Para imageNamed :, por otro lado, el filePath en el detalle de la celda se transmite textualmente.

Y la imagen de la fila es, naturalmente, la imagen que se carga. Entonces, para las filas en la tabla que no tienen imagen, la carga de la imagen falló .

Aquí, en pocas palabras, son los resultados. Si no lees nada de esta publicación, al menos un vistazo a esta imagen te dirá todo lo que necesitas saber.

Aquí está la explicación básica en puntos fácilmente digeribles:

  • como se indica en la documentación oficial, el método imageNamed: carga imágenes del paquete de aplicaciones. Esto significa que no necesita especificar la ubicación del paquete, solo el nombre del archivo. E incluso entonces, solo el nombre base del archivo. La documentación es un poco escasa aquí, realmente debería dejar en claro que carga la imagen de la ruta de archivo dada relativa al directorio raíz del paquete de aplicaciones.

  • (aquí está el truco, preste atención a este) esa regla sobre el directorio del paquete, refiere el directorio raíz en el paquete de su aplicación desplegada. Si vas a explorar, eso significa en el directorio ".app". Eso no es lo mismo que el directorio raíz del proyecto Xcode en el navegador Xcode, ni es lo mismo que el directorio raíz del proyecto Xcode en el buscador

  • esto se debe a que, al implementar su aplicación en el dispositivo (o simulador), todos los directorios de proyectos representados por " grupos para carpetas adicionales " se aplanan . Es decir, el directorio se ignora y todos sus contenidos se vuelcan sin ceremonias en el directorio raíz del paquete. (Digo "sin miramientos" porque si hay archivos con el mismo nombre en carpetas diferentes, colisionarán aquí y no obtendrá ayuda para resolver los problemas que provocan). Este es el caso de RealDir en mi ejemplo: en la aplicación desplegada, RealDir ya no existe, y icon2.png se deja mezclar con la población general (miedo). Casi ni que decir tiene que "JustGroup", el grupo Xcode puramente lógico, también se ignora, nunca fue un directorio real de todos modos, solo una ayuda visual para el usuario de Xcode, e icon1.png también está en la raíz del paquete.

    • Es por eso que imageNamed: pudo cargar icon2.

    • Y también por qué imageWithContentsOfFile no pudo encontrarlo en "RealDir / image2.png": porque no hay un directorio RealDir en la aplicación desplegada .

  • "carpetas azules", por otro lado, es decir, los directorios representados por " referencias de carpeta para carpetas añadidas ", de hecho se conservan en la estructura del directorio del paquete de la aplicación . Este, al parecer, es el punto de las carpetas azules: te dan una manera de crear una estructura de directorio en tu aplicación implementada. No estoy seguro de la razón de ser original para esto, pero un buen caso de uso es cuando tienes varios directorios que contienen versiones alternativas de archivos de recursos con el mismo nombre y quieres que tu aplicación pueda alternar entre ellos en tiempo de ejecución. cambiando el directorio. De todos modos, icon3.png en mi FolderReference, permaneció en mi directorio FolderReference en la aplicación desplegada.

    • Es por eso que imageNamed: no pudo encontrarlo con "icon3", pero pudo encontrarlo con "FolderReference / icon3"
    • imageWithContentsOfFile fue capaz de encontrarlo también utilizando FolderReference , pero solo cuando está conectado, recuerde la ruta completa del paquete utilizando el código anterior. (La diferencia clave aquí: imageNamed funciona con una ruta relativa en este caso, imageWithContentsOfFile siempre funciona con una ruta absoluta).

Para aclarar, aquí están mis estructuras de carpetas:

Viste la estructura del navegador de mi proyecto Xcode arriba, aquí está el directorio del sistema de archivos debajo de él:

Y finalmente, quizás lo más importante, la estructura del directorio del sistema de archivos del paquete implementado:

Nota: Encontré esto en esta ubicación en mi Mac: encontrará el suyo en una ubicación similar, es posible que deba buscar un bit para encontrar qué subdirectorio con el nombre GUID feo contiene su aplicación.

~/Library/Application Support/iPhone Simulator/5.1/Applications/4EB386B2-CD7E-4590-9757-18DDDEE6AF4F/ImageLoadingTest.app

Espero que esto ayude. Probando, explorando y finalmente, describiéndolo ciertamente me ayudó.

¿Qué tan necesario es buscar una ruta a una imagen usando el método pathForResource al crear un UIImage usando imageNamed ? Veo códigos de tutoriales que simplemente especifican el nombre de la imagen directamente, y luego el código que hace un esfuerzo adicional para encontrar la ruta primero.

En mi experiencia, siempre acabo de usar el nombre directamente y siempre funcionó bien. Supuse que automáticamente sabía cómo encontrar la imagen. ¿Qué tan importante o bajo qué circunstancias sería necesario hacer más que esto?


Prueba esto.

[UIImage imageNamed:@"your directory path of image"]

[UIImage imageNamed: @ "Dir1 / folder1 / folder2 / imagename.jpeg"]


Creé un nuevo proyecto Xcode (vista única, AppDelelgate, clase ViewController, guión gráfico, etc.). Creado un grupo de Imágenes. Usé Paintbrush para crear un archivo png de 16x16 Wall1.png y lo dejé caer en el grupo de Imágenes en Xcode (deje que Xcode copie los archivos).

En el método ViewController viewDidLoad, se agregó el código:

UIImageView* imageView=[[UIImageView alloc] initWithFrame:CGRectMake(50, 100, 16, 16)]; UIImage *image = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:@"Images/Wall1" ofType:@"png"]]; imageView.image = image; UIImageView* imageView=[[UIImageView alloc] initWithFrame:CGRectMake(50, 100, 16, 16)]; UIImage *image = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource:@"Wall1" ofType:@"png"]]; imageView.image = image; [self.view addSubview:imageView];

Corrí la aplicación en mi teléfono, la imagen no aparecería

Se agregó un punto de interrupción en [self.view addSubview: imageView];

la imagen era nula

El terminal abierto y el directorio cambiado a mi proyecto, Wall1.png no era una carpeta de grupo Imágenes. Se eliminó el png del proyecto, se creó una carpeta de imágenes y se movió a Wall1.png en la carpeta. Se agregó el archivo existente Wall1.png al grupo Imágenes.

Se ejecutó la aplicación, la imagen aún no aparece.

la imagen era nula

Imágenes cambiadas / Pared1 a Pared1

Se ejecutó la aplicación, se muestra la imagen del auge1

Si crea un grupo para sus imágenes, Xcode no crea un directorio respectivo. Cree uno manualmente si lo desea (prefiero mantener mis imágenes en una carpeta separada). No especifique la ruta completa a su archivo de imagen cuando use UIImage imageWithContentsOfFile.


Los documentos dicen que "el método busca una imagen con el nombre especificado en el paquete principal de la aplicación", por lo que yo diría que siempre puedes usar solo el nombre. La única excepción pueden ser las imágenes almacenadas dentro de las subcarpetas, especialmente cuando tiene foo/image.png y bar/image.png . No sé si [UIImage imageNamed:@"foo/image"] funcionaría, pero es trivial intentarlo.

(Lo que es un poco confuso en estos casos es que los grupos en el árbol de Xcode no se corresponden con las carpetas del paquete de aplicaciones resultante. Sus contenidos se unen en la raíz del paquete, a menos que utilice una referencia de carpeta azul en lugar de una normal grupo.)