什么是XIB,为什么我会使用一个?

如果您像我一样,则在查看代码时可能会看到这些“ .nib”和“ .xib”文件,并想知道它们到底意味着什么。 即使当我有人向我解释它们时,它们仍然没有多大意义,我也不知道为什么要使用它们。 但是由于这些东西在Swift中似乎比较流行,所以我决定对它们做更多的研究,然后搞一个新的项目,看看我是否能解决。

那么这些NIB和XIB是什么? 出于所有意图和目的,它们是同一件事–可在整个项目中重复使用的自定义视图。 实际上,XIB(XML接口生成器)在编译期间会转换为NIB(NeXTSTEP接口生成器),因为XIB易于人类阅读,而NIB则易于计算机阅读。 为了简洁起见,在博客的其余部分中,我将它们都称为“ XIB”。

好的,看来XIB可以帮助我们直观地创建视图,但这不是Storyboard的目的吗? 为什么使用其他类型的Interface Builder使事情复杂化? 好吧,实际上,XIB和情节提要板有很大的不同,即使它们经常相互结合使用也是如此。 XIB只是一个“事物”(一个自定义的,可重复使用的视图),而情节提要是许多“事物”(ViewController,TableViews,Segues等),主要用于导航和布局。

太酷了,所有这些都在概念上有意义,但是如何将其转化为代码? 我有同样的问题,它实际上对我自己的项目有很大帮助。 因此,我创建了一个(伪)音乐库,该库将在TableView中显示,并使该单元格成为创建为XIB的自定义单元格。 让我们进入代码…

等等,这不是XIB。 我以为我们正在学习如何创建和使用XIB! 是的,这也让我感到困惑,但实际上,我们首先需要创建一些东西,以使我们的XIB有意义。 由于我的应用程序要在音乐库中显示歌曲,因此我创建了一个Song结构(非常基本)以及一个MusicLibrary结构来保存我的所有歌曲:

现在我们有了一些基础集,我们实际上可以创建我们的XIB。 为此,您可以创建一个新文件并选择“查看”。这将带您进入一个新屏幕,如下所示:

对您来说,这应该看起来有些陌生,几乎就像一个情节提要。 尽管您可以使用许多与Storyboard相同的工具,但仍存在一些主要差异。 如您所见,主要的是XIB文件默认为视图,而不是ViewController。 不要被默认矩形形状与ViewController相同的事实所迷惑-实际上,让我们通过选择“大小”下的“自由形式”和“状态栏”下的“无”来改变它。更改自定义视图的大小,在我的情况下,我希望它是TableView Cell的大小(在Storyboard中查看以查看原型单元的大小)。

那里看起来好多了。 但是现在我们还需要将此XIB链接到代码中的某些内容,因此让我们使用UIView类(通常与XIB文件具有相同的名称)创建一个新文件,不要担心这不会混淆Xcode,因为是.xib,另一个是。swift)。

现在,就像在Storyboard中一样,我们需要创建一个插座以使连接完成。 在情节提要中,我们将从视图对象中执行此操作,对吗? XIB并非如此。 实际上,我们必须从“文件所有者”部分执行此操作:

完成后,我们可以将出口拖到我们的SongView.swift文件中:

现在,XIB变得有些混乱了。 我们可以通过两种方式初始化自定义视图-通过代码或通过Storyboard。 因此,我们需要两个初始化程序:带框架的初始化(用于代码)和带编码器的初始化(对于Storyboard):

但是,无论我们如何初始化视图,本质上我们都希望它以相同的方式进行设置,因此让我们创建一个可以为我们完成此操作的函数:

那里发生了什么事? 好吧,首先我们需要使用文件名实际加载XIB。 然后,我们要告诉Xcode我们希望自己设置约束,这就是“ translatesAutoresizingMaskIntoConstraints = false”行的作用。 下一行虽然很容易编写,但是起初有点复杂。 请记住,我们上面创建的SongView类本身就是一个UIView,而我们刚刚为其(我们的contentView)创建一个出口的视图也是一个UIView。 我们想将此contentView添加到SongView中,因此我们使用addSubview(contentView)。 我喜欢将其视为图层-SongView是我们的底层,而contentView现在位于其顶层,因此无论我们在何处添加SongView,contentView始终都在其中。 之后,我们只需添加约束以确保contentView始终约束在SongView的边缘。

! 好的,现在已经完成了很多繁重的工作,我们可以通过设置我们想要的实际视图看起来像什么来获得一些乐趣:

看起来不错。 请记住,这只是一个模板-那里的信息只是为了让我们可以看到视图的外观。 我们并不是说披头士乐队将每个单元“一起来”。 现在我们有了模板,我们可以将一些插座拖到SongView.swift文件中:

现在介绍酷的部分,实际上是使XIB如此有用的部分。 我们将这样做,以便我们负责将标签设置为远离TableViewController。 通常,我们将在cellForRowAt方法中执行一些操作,并在那里设置所有标签。 但是我们希望TableViewController是“哑巴的”,因为它只是做一件事并且做得很好-组织TableView。 传递给它的信息是另一个任务,因此我们将自己设置SongView:

因此,我们首先创建一个名为“ song”的变量,我们知道它将是Song类型(我们首先创建的结构)。 然后,我们使用didSet观察器将单元格上的所有信息设置为Song中的信息。 这意味着,每当将新歌曲设置为我们的“ song”变量时,didSet的代码就会被触发(在我们的示例中,它会设置albumImage和所有标签)。 很酷吧?

最后一步(或几个步骤)是使我们的自定义视图出现在TableView中。 为了简单起见,我将在情节提要中进行操作。 请记住,我们使自定义视图的大小与原型单元格相同,因此让我们向该单元格添加一个视图并将其约束到边缘(因此使其与自定义视图的大小相同):

我更改了背景颜色,以便您可以看到我正在使用它-我最终将其更改为白色。 由于我们希望将其连接到我们的SongView中,因此我们将其添加为自定义类。 由于我们已经创建了一个自定义单元,因此我们需要创建一个反映这一点的文件,因此让我们添加一个名为“ SongCell”的文件,该文件的类型为UITableViewCell并将视图从Storyboard拖到该文件上:

现在,故事板已全部设置好。 如果您仍然对正在发生的事情感到困惑,那么它可以帮助我查看侧栏中的事情流程:

我们可以看到我们有一个TableViewController,上面有一个TableView。 该TableView上有一个名为“ SongCell”的(自定义)单元,该单元上有一个Content View(默认情况下),我们在其中添加了自己的自定义“ SongView”。

现在,所有内容都通过XIB和Storyboard进行了设置,我们所需要做的就是设置MusicTableViewController:

哇。 看起来很干净’TableViewController! 这里发生了什么事? 首先,我们设置一个名为“ musicLibrary”的变量,这只是我们创建的名为MusicLibrary的结构(只是一串歌曲)的一个实例。 然后,我们将行数设置为等于该musicLibrary.songs数组的数量(在我们的示例中为10,因为这是我创建的歌曲数量)。 现在是最酷的部分。 通常,我的cellForRowAt方法充满了label.text和imageView.image等。但是在这里,它非常干净和简单。 我像往常一样设置了单元,使用单元标识符出队并将其强制转换为自定义SongCell。 然后,我将当前歌曲(要在单元格中显示的那首歌曲)设置为indexPath.row上musicLibrary歌曲数组中的歌曲。 而且我们知道,由于将单元格强制转换为自定义的SongCell,所以它上面有一个名为“ songView”的出口(这是我们在Storyboard中创建的视图),其类型为SongView,而且我们知道SongView的所有实例都具有一个属性称为“歌曲”,因此我们可以将其设置为当前歌曲。 现在,我们的didSet被解雇,我们的信息被建立! 通过使用“ cell.songView.song”,我们正在设置一首新歌,并告诉我们的歌曲变量每次设置后都要执行一些操作,即更新图像和标签。 很酷的东西。

让我们看一下并确保它起作用:

您可能会想:“等等,这是什么意思? 自从我们执行了出队操作以来,tableview单元格不是可以自然地重用吗?”而您将是对的。 使用XIB的主要原因之一是创建自定义和可重用的视图。 但是另一个主要原因是要分离出逻辑,并确保我们的功能能够完成一件事并做到最好。 在我们的例子中,我们从ViewController中分离出了逻辑(使其变得笨拙),而是将在单元格上设置信息的责任交给了我们的SongView。 还很酷的是,如果我们的应用程序中某个地方有另一个TableView,我们也可以在那里重复使用自定义视图,而不必在Storyboard中一次又一次地手动创建它。

XIB非常酷,尽管我花了很长时间才弄清楚它们,但我还是希望将它们更多地集成到我的项目中。 我希望这会有所帮助,并且像Swift中的所有新概念一样,它有助于打开一个新项目并弄乱事物。