得益于Interface Builder,苹果大大简化了iOS开发者创建复杂UI的过程。创建一个UIViewController,选择同时创建一个nib文件,然后开始使用所有的预构建UIView组件进行编辑:UIButton、UILabel、UIScrollView、UITableView等简化了您的生活。Interface Builder允许您根据需要排列组件,更改其属性,并将它们连接到IBOutlets和IBActions。
然而,对于创建自己的UIView组件,没有使用Interface Builder图形创建组件布局的标准方法。您需要编写许多行代码来安排现有组件到视图层次结构中,仅更改它们的属性,却发现自己编写的代码并没有达到预期的布局。这是一个繁琐的过程,而且让人们创建自己的UIView组件很难,因为创建它们并不有趣。
为了简化实现UIView组件的过程,我们创建了NMView。NMView是UIView的子类,它定义了一种将nib文件与视图关联的标准机制,以及如何将其内容加载到视图中。此外,它还提供了诸如在nib文件中指定不同的视图布局(例如,横向与纵向)等高级功能,这些布局可以根据视图的宽高比手动或自动激活。
在我们成功在一个项目中使用NMView之后,我们终于将其源代码在BSD许可证下发布出来!
一个完整的Xcode项目!在Xcode中打开,浏览项目分组,选择一个可用的目标,并在iOS模拟器中运行,以了解NMView可以达成什么效果。然后深入研究样例,看看它们是如何工作的。我尽量详细地描述了样例,并在NMView类本身中添加了注释。
项目包含NMView和required classes在Libraries/NMView文件夹中。只需将整个目录复制到您自己的项目中,然后开始创建NMView子类。
请查看不同示例组中的NMView子类。每个示例都展示了NMView的特定功能,可以作为您自己NMView子类实现的参考。
Sergei(在过去的几周里广泛地使用了NMView)为我们提供了有关NMView背后的思想及其使用方式的更多细节。
基本思想是,一个NMView子类像一个小型视图控制器,可能有自己的nib文件和自己的逻辑。为进一步简化,您只需要遵循几个基本步骤:
“从NIB加载”示例展示了这一点。
先前,NMView仅支持一种方法来根据视图边界的更改来更新视图内容:使用NMView加载的nib中定义的备用布局。现在,这种备用布局的管理已正式化成NMViewLayoutManager及其子类NMExplicitLayoutManager。
此外,还创建了一个具有辅助布局子视图进入网格结构的NMViewLayoutManager子类。
布局管理器本身是独立于NMView的,可以在不同的场景中重用。
此外,NMView使得在界面方向间自动旋转变得更容易。为了实现这一点,nib文件应该包含几个类为NMViewLayoutView的顶级视图。(参见AutomaticLayoutChangesView.xib以获取示例。)当NMView对象的frame发生变化时,视图将通过找到新的长宽比并选择最接近的布局来调整其布局。由于备用布局是在Interface Builder中创建的,您不需要手动编写很多代码。
一个值得注意的例外是,当一些UIImageView需要在不同方向布局中使用不同的位图时。在这种情况下,必须在-overridden method -layoutDidChangeToAspectRatio:方法中显式分配位图。
重要的是要注意,nib文件中的第一个视图才是您应用程序中真正使用的视图。所有备用布局视图仅用于为第一个视图的子视图分配框架。如果某些视图需要在使用竖直布局时显示红色背景,而在横屏模式下显示绿色背景,则需要手动在-by -layoutDidChangeToAspectRatio:中编码。当前版本NMview中的代码将忽略备用视图中设置的背景颜色。备用视图的任何其他属性也将被忽略。
需要注意,NMView 对象不会收到方向变化的消息。NMView 对象不是一个视图控制器,而是一个 UIView 子类,通常会是某个 UIViewController 的视图。在旋转时,控制器会调整它的视图大小,NMView 将会接收到视图大小改变的消息。如果启用了自动布局变化,将会调用 layoutSubviewsIfNecessary 函数,并且可能会选择另一个布局。如果禁用了自动布局变化,layoutSubviewsIfNecessary 函数不会被调用,布局也不会自动变动。你可以在控制器的 –willAnimateRotationToInterfaceOrientation:duration: 方法中自己调用此函数。
我们可以禁用自动布局变化的原因是:当一些子视图(例如动画按钮)在某个子视图中(例如缩放或旋转发生改变)进行动画处理时,每一帧都会在父视图中调用 layoutSubviews。如果启用了自动布局变化,这将会导致动画崩溃,因为当子视图正在动画时,NMView 会将子视图的框架设定为初始位置。因此,如果 NMView 的任何直接子视图正在通过 Core Animation 进行缩放或旋转,则必须禁用自动布局变化。
如果你需要在你的视图中动态显示具有相同布局的内容,并且在不同的数据实例之间有相同的布局,你可能需要查看视图模板。
在 UIView+NMTemplating.h / .m 分类中,-applyViewTemplate: 方法允许你将传入参数的视图的所有属性和子视图应用到调用该方法的对象上。调用完成时,接收器将正好按照你定义的视图模板看起来一样。你现在可以做的是使用数据配置新的视图,并将其添加到视图中。
作为替代,你始终可以创建模板视图作为常规的 UIView / NMView 子类,并在需要显示新数据时创建该类的新的实例。使用视图模板而不是自定义 NMView 子类的好处是加载时间:每次创建新的 NMView 实例时,nib 文件都会从磁盘加载到内存中。使用视图模板时,你可以在 NMView 的 nib 文件中指定模板,通过 IBOutlet 赋予你的 NMView 子类,并从内存中的该模板进行工作。创建模板的新实例只是调用 [[[UIView alloc] initWithFrame:frame] applyViewTemplate:theTemplate]
。