16

创建using时使用该NSBundle方法搜索图像路径的必要性有多大?我看到的教程代码只是直接指定图像的名称,然后代码会更加努力地首先找到路径。pathForResourceUIImageimageNamed

以我的经验,我总是直接使用这个名字,而且它总是很好用。我假设它自动知道如何找到图像。有必要做更多的事情或在什么情况下需要做更多的事情?

4

4 回答 4

65

根本不是......是原始问题的答案:

使用 imageNamed 创建 UIImage 时,使用 NSBundle 方法 pathForResource 搜索图像路径的必要性有多大?

Zoul和 Ranga 接受的答案有多正确。公平地说:如果您谈论的是应用程序包目录结构,或者图像位于 Xcode 中的“蓝色”文件夹中的(罕见)情况(稍后会详细介绍),它们是正确的,但不是大多数常见案例

无论如何,找到一个真实的答案。

像往常一样,我在尝试自己寻找答案时发现了这个问题。我从来没有找到令人满意的文档或这个问题的其他答案,所以我决定测试一下。

我的测试细节都在下面,但让我在这里总结一下结果。简而言之,当使用 imageNamed: 加载图像时,这取决于您将它们放在哪里:

  1. 如果您的图像位于项目的根目录中,即使组织在一个纯粹的逻辑 Xcode 组中,那么不,您不需要考虑路径:只需考虑图像名称。

  2. 如果您的图像位于附加到文件系统目录的组中,通过“为添加的文件夹创建组”,那么您仍然不需要担心名称。

  3. 如果您的图像位于“蓝色”组中,该组通过“为添加的文件夹创建文件夹引用”附加到文件系统中的目录,那么您可以使用 imageNamed: 通过指定建议的相对路径来加载它(巧合?)通过上面接受的答案。

  4. 如果您使用 imageNamed:、imageWithContentsOfFile: 的主要替代方法,您确实需要包含包路径的文件的完整路径,这意味着您需要知道 Xcode 导航器结构如何转换为包目录结构中的路径。

这两种方法之间的其他重要区别:

  • imageNamed 不需要您指定文件类型扩展名,因此只需“icon”而不是“icon.png”,而 imageWithContentsOfFile 确实需要完整的文件名
  • 这第一点有助于第二个功能: 如果有图像,imageNamed 将通过在文件名中添加@2x 来自动加载图像的视网膜版本。因此,如果您要求“图标”,在视网膜显示器上它会尝试加载“icon@2x.png”。imageWithContentsOfFile 没有
  • imageNamed 缓存图像:其中存在很多争议:如果您搜索 SO 或整个网络,您会发现很多帖子建议您避免使用它,因为它没有正确清除其缓存。但是,这在几年前就已修复,因此您不必担心它无法清除缓存。不过,您仍然需要担心它会缓存这一事实。如果您的图像很大并且不经常加载,您可以通过从文件中加载它们而不是缓存它们来节省内存。这与泄漏无关:即使您没有泄漏,设备上的内存仍然有限,并且您不想进行不必要的缓存。这是经典的缓存权衡:什么 在你的情况下更重要吗?内存性能或cpu性能(时间)。

继续我的测试。

我所做的是创建一个简单的 UITableView 应用程序,其中包含 3 个简单的图标文件,使用不同的方法显示在表格的行中。图标在Xcode中的位置不同项目结构。请注意对 Xcode 的强调。理解原始问题答案的关键在于,iOS 应用程序中存在三种完全不同的项目目录结构:一种是您在 Xcode 导航器中看到的,另一种是您在 Finder 中看到的同一项目的文件系统(右键单击 Xcode 导航器中的任何项目并选择“在 Finder 中显示”)以及您很少看到的已部署应用程序的“捆绑”目录结构。您也可以在 Finder 中看到最后一个 - 通过在 ~/Library/Application Support/iPhone Simulator 中找到您的应用程序,然后深入到 .app 目录。一会儿我给你看一张我的照片。

所以在我的应用程序中,我以不同的方式将所有三个图标 png 图像文件拖到 Xcode 中:

  1. icon1.png(一个时钟),我作为文件拖入 Xcode 项目的根目录,然后我在 Xcode创建了一个新组并将其拖入其中。该组不由文件系统中的任何目录表示:它是一个纯 Xcode 组。因此它的名字是:“JustGroup”

  2. icon2.png(一只眼睛),我最初将我的文件系统放在名为“RealDir”的目录中,然后我将整个目录拖到 Xcode 中,当被问到时,我选择了“为任何添加的文件夹创建组”选项。这意味着 Xcode 中的 RealDir 组附加到文件系统中名为 RealDir 的真实目录(在我的项目目录中),并且 icon2.png 在那里。

  3. icon3.png (一个目标),我也有一个单独的目录,我也将它拖到 Xcode 中。只有这一次我选择了第二个单选选项“为任何添加的文件夹创建文件夹引用”。这在 Xcode 中创建了一个所谓的“蓝色”组。更多关于这一切的内容稍后。我称这个组(和目录)为“FolderReference”

这是 Xcode 为您提供的选择的截图: 拖动目录时 Xcode 的对话框

这是我的项目结构在 Xcode 中的样子: Xcode 导航器项目结构

现在,在我的应用程序中,我使用了两种方法来加载每个图标: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 是逐字传递的。

图像自然是加载的图像。所以对于表中没有图片的行,图片加载失败

简而言之,这里是结果。如果你没有阅读这篇文章,至少看一眼这张图片就会告诉你所有你需要知道的。

应用程序显示加载了哪些图标

以下是易于理解的基本解释:

  • 正如官方文档中所述, imageNamed: 方法从应用程序包中加载图像。这意味着您不需要指定捆绑位置,只需指定文件名。即使这样,也只是文件的基本名称。这里的文档有点薄,它应该清楚地说明它从对于应用程序包根目录的给定文件路径加载图像。

  • (这是踢球者,注意这一点)关于捆绑目录的规则,指的是您部署的应用程序捆绑包中的根目录。如果你去探索,那意味着在“.app”目录本身。和Xcode navigator中Xcode项目的根目录不一样,也不finder中Xcode项目的根目录一样

  • 这是因为,在将您的应用程序部署到设备(或模拟器)时,由“添加文件夹的组”表示的所有项目目录都被展平. 也就是说,该目录将被忽略,并且其所有内容都会毫不客气地转储到捆绑包的根目录中。(我说“毫不客气”,因为如果不同文件夹中有同名文件,它们会在这里发生冲突,您将无法解决导致的问题。)在我的示例中,RealDir 就是这种情况:部署的应用程序,RealDir 不再存在,icon2.png 与普通人群混在一起(可怕)。几乎不用说,“JustGroup”,纯粹的逻辑 Xcode 组,也被忽略了——它从来都不是一个真正的目录,只是 Xcode 用户的视觉帮助——而且 icon1.png 也在包根目录中。

    • 这就是为什么 imageNamed: 能够加载 icon2。

    • 还有为什么 imageWithContentsOfFile 无法在“RealDir/image2.png”中找到它:因为部署的应用程序中没有 RealDir 目录

  • 另一方面,“蓝色文件夹”,即“添加文件夹的文件夹引用”表示的目录,实际上保留在 app bundle 目录结构中。这显然是蓝色文件夹的重点:它们为您提供了一种在部署的应用程序中创建目录结构的方法。我不确定最初的存在理由,但一个很好的用例是您有多个目录,其中包含具有相同名称的资源文件的替代版本,并且您希望您的应用程序能够在运行时在它们之间切换通过更改目录。无论如何,我的 FolderReference 中的 icon3.png 仍保留在已部署应用程序的我的 FolderReference 目录中。

    • 这就是为什么 imageNamed: 无法使用“icon3”找到它,但可以使用“FolderReference/icon3”找到它
    • imageWithContentsOfFile 也能够使用 FolderReference 找到它,但只有在附加时,请记住使用上面的代码找到完整的包路径。(这里的关键区别:在这种情况下,imageNamed 使用相对路径,imageWithContentsOfFile 始终使用绝对路径)。

为了澄清,这是我的文件夹结构:

您在上面看到了我的 Xcode 项目导航器结构,这是它下面的文件系统目录: Finder Xcode 项目目录结构

最后,也许是最重要的,部署的包文件系统目录结构: 部署的bundle目录结构

注意:我在我的 Mac 上的这个位置找到了这个:你会在一个类似的位置找到你的 - 你可能需要搜索一下才能找到哪个丑陋的 GUID 命名的子目录包含你的应用程序。

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

我希望这有帮助。测试、探索并最终描述它确实对我有帮助。

于 2012-10-17T10:43:15.423 回答
5

文档说“该方法在应用程序的主包中查找具有指定名称的图像”,所以我想说你总是可以只使用名称。唯一的例外可能是存储在子文件夹中的图像,尤其是当您拥有foo/image.pngbar/image.png. 我不知道是否[UIImage imageNamed:@"foo/image"]会奏效,但尝试起来很简单。

(在这些情况下有点令人困惑的是 Xcode 树中的组不对应于生成的应用程序包中的文件夹。它们的内容被一起粉碎到包的根目录,除非您使用蓝色文件夹引用而不是常规团体。)

于 2012-04-30T08:54:20.703 回答
0

我创建了一个新的 Xcode 项目(单视图、AppDelelgate、ViewController 类、情节提要等)。创建了一个图像组。使用 Paintbrush 创建一个 16x16 png 文件 Wall1.png 并将其放入 Xcode 中的 Images 组(让 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] 处添加断点;

图像为空

打开终端并将目录更改为我的项目,Wall1.png 不是组文件夹图像。从项目中删除了 png,创建了一个 Images 文件夹,将 Wall1.png 移动到该文件夹​​中。将现有文件 Wall1.png 添加到图像组。

运行应用程序,图像仍然没有出现。

图像为空

将图像/Wall1 更改为 Wall1

运行应用程序,显示景气图像1

如果为图像创建组,Xcode 不会创建相应的目录。如果您愿意,可以手动创建一个(我更喜欢将图像保存在单独的文件夹中)。使用 UIImage imageWithContentsOfFile 时不要指定图像文件的完整路径。

于 2015-01-30T14:49:15.513 回答
-1

尝试这个。

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

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

于 2012-04-30T09:13:54.770 回答