UIImage imageNamed требует pathForResource?

Насколько необходимо искать путь к изображению с помощью метода NSBundle pathForResource при создании UIImage с помощью imageNamed? Я вижу коды учебников, которые просто определяют имя изображения напрямую, а затем код, который проходит лишнюю милю, чтобы сначала найти путь.

По моему опыту, я всегда просто использовал имя напрямую, и он всегда работал нормально. Я предположил, что он автоматически знал, как найти изображение. Насколько важно или при каких обстоятельствах было бы необходимо сделать больше, чем это?

Ответ 1

Документы говорят, что "метод ищет изображение с указанным именем в главном пакете приложений", поэтому Id говорит, что вы всегда можете использовать только имя. Единственным исключением могут быть изображения, хранящиеся внутри подпапок, особенно если у вас есть foo/image.png и bar/image.png. Я не знаю, будет ли работать [UIImage imageNamed:@"foo/image"], но его тривиально попробовать.

(В этих случаях немного сбивает с толку то, что группы в дереве Xcode не соответствуют папкам в результирующем пакете приложений. Их содержимое разбивается на корень пакета, если вместо этого вы не используете ссылку на синюю папку регулярной группы.)

Ответ 2

Не совсем... это ответ на исходный вопрос:

Насколько необходимо искать путь к изображению с помощью метода NSBundle pathForResource при создании UIImage с использованием imageNamed?

Не намного. Насколько правильно принят принятый ответ от Zoul и другой из Ranga. Справедливости ради: они правильны, если вы говорите о структуре каталогов пакетов приложений или для (редкого) случая, когда изображение находится в "голубой" папке в Xcode (подробнее об этом позже), но не для большинства общие случаи

В любом случае, к одному истинному ответу.

Как обычно, я нашел этот вопрос, пытаясь найти ответ сам. Я никогда не нашел документацию или другие ответы на этот вопрос удовлетворительными, поэтому я решил проверить.

Мои данные теста все ниже, но позвольте мне обобщить результаты здесь. Короче говоря, при использовании imageNamed: для загрузки изображений это зависит от того, где вы их помещаете:

  • если ваши изображения находятся в корне вашего проекта, даже если они организованы в чисто логической группе Xcode, тогда нет, вы не нужно подумать о пути: просто имя изображения.

  • если ваши изображения находятся в группе, прикрепленной к каталогу в вашей файловой системе, через "создавать группы для добавления папок", вам все равно не нужно беспокоиться о имени.

  • если ваши изображения находятся в "голубой" группе, прикрепленной к каталогу в вашей файловой системе через "создавать ссылки на папки для добавленных папок", вы можете загрузить его с помощью imageNamed: указав относительный путь как было предложено (по совпадению?) принятым ответом выше.

  • если вы используете основную альтернативу imageNamed:, imageWithContentsOfFile: вам действительно нужен полный путь к файлу, включая путь пакета, что означает, что вам нужно знать, как структура навигатора Xcode переводится в пути в вашем структура каталога пакетов.

Другие важные различия между этими двумя методами:

  • imageNamed не требует, чтобы вы указали расширение типа файла, поэтому просто "значок" не "icon.png", тогда как imageWithContentsOfFile делает требуется полное имя файла
  • эта первая точка помогает со второй функцией: imageNamed будет автоматически загружать версию сетчатки изображения, если она есть, добавив @2x к имени вашего файла. Поэтому, если вы попросите "значок", на Retina Display, он попытается загрузить "[email protected]". imageWithContentsOfFile не
  • imageNamed кэширует изображение: в нем много споры вокруг него: если вы ищете SO или Интернет в целом, вы будете найти много сообщений, рекомендующих вам избежать этого, потому что это не правильно очистите его. Это, однако, было исправлено несколько лет назад, поэтому вы не нужно беспокоиться о том, что он не очистил свой кеш. Вы все еще нужно беспокоиться о том, что он вообще кэширует. Если ваш изображения большие и не загружаются очень часто, вы сохраните память, загружая их из файла, а не кэшируя их. Это не имеет ничего общего с утечками: даже если у вас нет утечек, вы все равно имеют ограниченную память на устройстве, и вы не хотите кэшировать без необходимости. Это классический компромисс кэширования: что еще важно в вашей ситуации? Производительность памяти или производительность процессора (Время).

Итак, мои тесты.

Я создал простое приложение UITableView с тремя простыми файлами значков, показанными в строках таблицы, используя разные методы. Значки различаются по своему расположению в структуре проекта Xcode. Обратите внимание на акцент на Xcode. Ключом к пониманию ответа на исходный вопрос является то, что в приложении iOS есть три совершенно разные структуры каталогов проекта: там вы видите в навигаторе Xcode, тот, что в файловой системе для того же проекта, который вы видите в Finder (щелкните правой кнопкой мыши любой элемент в навигаторе Xcode и выберите "показать в Finder" ) и, как вы редко видите, структуру каталогов "bundle" развернутого приложения. Вы также можете увидеть эту последнюю в Finder - найдя свое приложение в ~/Library/Application Support/iPhone Simulator и свернув в каталог .app. Я покажу вам свою фотографию через минуту.

Итак, в моем приложении я перетащил все три файла изображений png в Xcode по-разному:

  • icon1.png(часы), я перетащил его как файл в корень проекта Xcode, затем я позже создал новую группу в Xcode и перетащил ее в что. Эта группа не представлена ​​никаким каталогом в файле system: это чистая группа Xcode. Следовательно, это имя: "JustGroup"

  • icon2.png(глаз), я изначально поставил свою файловую систему в каталог, называемый "RealDir", и я перетащил весь этот каталог в Xcode, и когда спросил, я выбрал вариант "Создать группы для любых добавленных папок". Это означает, что группа RealDir в Xcode привязана к реальному каталог под названием RealDir в файловой системе (в моем каталоге проектов) и там находится значок2.png.

  • icon3.png(цель), я также имел в отдельном каталоге, который я также перетаскивал в Xcode. Только на этот раз я выбрал второй вариант радио "Создать ссылки на папки для любых добавленных папок ". Это создает так называемый" синяя" группа в Xcode. Подробнее о том, что это все о позже. я называется эта группа (и каталог) "FolderReference"

Вот выбор из того, что Xcode дает вам: Xcode's dialog when dragging a directory in

И вот как выглядит моя структура проекта в Xcode: Xcode navigator project structure

Теперь, в моем приложении, я использовал два метода для загрузки каждого из значков: UIImage imageNamed: и UIImage imageWithContentsOfFile. Я создал ряд строк в моей таблице, причем заголовок каждой ячейки является именем группы, содержащей значок: JustGroup, RealDir или FolderReference, плюс имя используемого метода: imageNamed vs fromFile (который я использую как аббревиатура imageWithContentsOfFile)

Метка ярлыка ячейки (более слабый текст под заголовком) показывает имя файла или пути, которые я дал методу.

Чтобы быть ясным, в случае с "fromFile" я добавляю путь пакета к "относительному" имени, которое вы видите. Поэтому для "fromFile" я действительно использую этот код:

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

где "filePath" - это путь, который вы видите на ярлыке ячейки таблицы. Для imageNamed:, с другой стороны, filePath в элементе ячейки передается дословно.

И образ строки - это, естественно, загруженное изображение. Поэтому для строк в таблице, у которых нет изображения, загрузка изображения не удалась.

Здесь, в двух словах, есть результаты. Если вы ничего не читаете об этом посту, по крайней мере взгляд на это изображение скажет вам все, что вам нужно знать.

app shows which icons were loaded

Здесь основное объяснение в легко усваиваемых точках:

  • как указано в официальной документации, метод imageNamed: загружает изображения из набора приложений. Это означает, что вам не нужно указывать местоположение пакета, только имя файла. И даже тогда просто базовое имя файла. Документация здесь немного тонкая, поэтому должно быть ясно, что она загружает изображение из заданного пути файла относительно корневого каталога приложения.

  • (здесь, обратите внимание на кикер, это правило) о том, что правило о каталоге пакетов, ссылается на корневой каталог в вашем комплекте вашего развернутого приложения. Если вы исследуете, это означает, что в самом каталоге ".app". Это не то же самое, что корневой каталог проекта Xcode в навигаторе Xcode, и он не совпадает с корневым каталогом проекта Xcode в finder

  • это связано с тем, что при развертывании вашего приложения на устройстве (или симуляторе) все каталоги проектов, представленные "группами для добавленных папок", сглаживаются. То есть каталог игнорируется, и все его содержимое бесцеремонно выгружается в корневой каталог пакета. (Я говорю "бесцеремонно", потому что, если в разных папках есть файлы с одинаковым именем, они будут сталкиваться здесь, и вы не получите никакой помощи в разрешении возникающих проблем.) Это пример RealDir в моем примере: in развернутое приложение, RealDir больше не существует, и icon2.png остается смешаться с общей популяцией (страшно). Это почти не говорит о том, что "JustGroup", чисто логическая группа Xcode, также игнорируется - в любом случае она никогда не была настоящим каталогом, просто визуальная помощь пользователю Xcode, а icon1.png также входит в корень пакета.

    • Вот почему imageNamed: удалось загрузить значок2.

    • Кроме того, почему imageWithContentsOfFile не удалось найти его в "RealDir/image2.png": поскольку в развернутом приложении нет каталога RealDir.

  • "голубые папки", с другой стороны, то есть каталоги, представленные "ссылками на папки для добавленных папок", фактически сохраняются в структуре каталогов приложений. Это, по-видимому, точка синих папок: они дают вам способ создать структуру каталогов в развернутом приложении. Я не уверен в оригинальном raison d'etre для этого, но один хороший пример использования - это то, где у вас есть несколько каталогов, содержащих альтернативные версии файлов ресурсов с тем же именем, и вы хотите, чтобы ваше приложение могло переключаться между ними во время выполнения путем изменения каталога. Во всяком случае, icon3.png в моем FolderReference, остался в моем каталоге FolderReference в развернутом приложении.

    • Вот почему imageNamed: не удалось найти его с помощью значка "icon3", но можно найти его с помощью "FolderReference/icon3"
    • imageWithContentsOfFile смог найти его также с помощью FolderReference, но только при подключении не забудьте указать полный путь пакета, используя приведенный выше код. (Основное отличие здесь: imageNamed работает с относительным путем в этом случае, imageWithContentsOfFile всегда работает с абсолютным путем).

Чтобы уточнить, вот мои структуры папок:

Вы видели мою навигационную структуру проекта Xcode выше, вот каталог файловой системы под ней: Finder Xcode project directory structure

И, наконец, возможно, самое главное, структура каталогов файловой системы с развернутой структурой: deployed bundle directory structure

Примечание. Я нашел это в этом месте на моем Mac: вы найдете свое место в аналогичном месте - вам, возможно, придется немного поискать, чтобы найти подкаталог с уродливым GUID-именем, содержащий ваше приложение.

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

Надеюсь, это поможет. Тестирование, изучение и, наконец, его описание, безусловно, помогли мне.

Ответ 3

Я создал новый проект Xcode (один вид, AppDelelgate, класс ViewController, раскадровку и т.д.). Создана группа изображений. Использовал Paintbrush, чтобы создать файл PN1.xng 16x16 и упал его в группу "Изображения" в Xcode (пусть Xcode скопирует файлы).

В ViewController viewDidLoad добавлен код:

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];

Измените приложение на моем телефоне, изображение не появится

Добавлена ​​точка останова в [self.view addSubview: imageView];

Изображение было null

Открыл терминал и изменил каталог в моем проекте, Wall1.png не был папкой группы "Изображения" . Удалил png из проекта, создал папку "Изображения" , переместил Wall1.png в папку. Добавлен существующий файл Wall1.png в группу Images.

Запустите приложение, изображение все еще не отображается.

Изображение было null

Измененные изображения /Wall 1 to Wall1

Запустите приложение, появится изображение стрелы1

Если вы создаете группу для своих изображений, Xcode не создает соответствующий каталог. Создайте его вручную, если хотите (я предпочитаю, чтобы мои изображения были в отдельной папке). Не указывайте полный путь к файлу изображения при использовании UIImage imageWithContentsOfFile.

Ответ 4

Попробуйте это.

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

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