iPhone:创build一个可重用的组件(控件),它具有一些Interface Builder部分和一些代码

我想为iPhone创build一个可重用的组件(一个自定义控件)。 它由几个预先安排在View上的标准控件组成,然后是一些相关的代码。 我的目标是:

  1. 我想能够使用Interface Builder在我的自定义控件中布局子视图;
  2. 我想以某种方式打包整个事情,这样我就可以很容易地将生成的自定义组件拖放到其他视图中,而无需手动重新连接一堆sockets等等。 (稍微手动重新连线是好的,我只是不想做吨和吨。)

让我更具体一点,并具体告诉你我的控制应该做什么。 在我的应用程序中,我有时需要访问Web服务来validation用户input的数据。 在等待Web服务的回复时,我想显示一个微调(一个活动指示器)。 如果networking服务回复成功代码,我想显示一个“成功”复选标记。 如果Web服务使用错误代码回复,我想显示错误图标和错误消息。

一次性使用的方法很简单:我只是创build一个包含UIActivityIndi​​catorView的UIView,两个UIImages(一个用于成功图标,另一个用于错误图标),以及一个用于错误消息的UILabel。 这是一个截图,相关部分用红色标出:

替代文字

然后我将这些部分连接到网点,然后在控制器中放入一些代码。

但是,我如何打包这些代码 – 代码和less量视图 – 以便我可以重用它们? 以下是我发现的一些事情,让我在那里,但不是那么好:

  • 我可以将视图和控件的集合拖入库的“自定义对象”部分; 之后,我可以将它们拖回到其他视图上。 但是(a)它忘记了哪些图像与这两个UIImage相关,(b)有四五个网点有很多手动重新布线,(c)最重要的是,这不会带来代码。 (也许有一个简单的方法来连接代码?)
  • 我想我可以创build一个IBPlugin; 不知道这是否会有所帮助,而且看起来还有很多工作要做,IBPlugins是否适用于iPhone开发还不太清楚。
  • 我想,“嗯,有与此相关的代码 – 闻起来像一个控制器,”所以我尝试创build一个自定义控制器(如WebServiceValidatorController )与关联的XIB文件。 这实际上是非常有前途的,但是到那时我无法弄清楚在Interface Builder中如何将这个组件拖到其他视图上。 WebServiceValidatorController是一个控制器,而不是一个视图,所以我可以将它拖到一个文档窗口,但不是一个视图。

我有一种感觉,我失去了明显的东西…

我创build了类似的结构,只是我不使用IB中的结果,而是使用代码实例化。 我将描述这是如何工作的,最后我会给你一个提示,告诉你如何使用它来完成你的工作。

我从一个空的XIB文件开始,我在顶层添加一个自定义视图。 我将该自定义视图configuration为我的类。 在视图层次结构下面,根据需要创build和configuration子视图。

我在自定义视图类中创build了所有的IBOutlet,并将它们连接到那里。 在这个练习中,我完全忽略了“文件的所有者”。

现在,当我需要创build视图(通常在控制器中作为while / for-loop的一部分来创build尽可能多的视图),我使用NSBundle的function如下:

 - (void)viewDidLoad { CGRect fooBarViewFrame = CGRectMake(0, 0, self.view.bounds.size.width, FOOBARVIEW_HEIGHT); for (MBSomeData *data in self.dataArray) { FooBarView *fooBarView = [self loadFooBarViewForData:data]; fooBarView.frame = fooBarViewFrame; [self.view addSubview:fooBarView]; fooBarViewFrame = CGRectOffset(fooBarViewFrame, 0, FOOBARVIEW_HEIGHT); } } - (FooBarView*)loadFooBarViewForData:(MBSomeData*)data { NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"FooBarView" owner:self options:nil]; FooBarView *fooBarView = [topLevelObjects objectAtIndex:0]; fooBarView.barView.amountInEuro = data.amountInEuro; fooBarView.barView.gradientStartColor = data.color1; fooBarView.barView.gradientMidColor = data.color2; fooBarView.titleLabel.text = data.name; fooBarView.titleLabel.textColor = data.nameColor; return fooBarView; } 

请注意,我是如何将笔尖的主人设置为self – 没有任何伤害,因为我没有将“文件所有者”连接到任何东西。 加载这个笔尖的唯一有价值的结果是它的第一个元素 – 我的观点。

如果您想要在IB中使用这种方法来创build视图,那很简单。 在主自定义视图中实现子视图的加载。 将子视图的框架设置为主视图的边界,以使它们具有相同的大小。 主视图将成为您真实自定义视图的容器,也是外部代码的接口 – 只打开其子视图的必需属性,其余部分则封装。 然后,您只需在IB中放置自定义视图,将其configuration为您的类,然后像往常一样使用它。

以下是我如何解决类似的问题:我想:

  • 创build可重用,自包含的“小部件模块类”,实现由多个UIKit组件(和其他嵌套的小部件模块类)构build的复杂视图。 这些小部件类的更高级的客户类不关心什么是UILabel,小部件内部是什么UIImageView,客户类只关心诸如“显示分数”或“显示团队标识”这样的概念。

  • 在一个小部件模块类中,使用接口生成器为小部件和连接sockets布局UI

  • 对于小部件的更高级别的客户,我希望能够在界面构build器中将小部件的框架放置在客户视图中,而不必为IBdevise自定义插件等。

这些小部件不是顶级控制器:所以它们成为UIViewController的子类是没有意义的。 另外还有苹果的build议,一次只能在一个可见的屏幕上使用不止一个VC。 另外,还有如此多的build议和意见,比如“MVC!MVC!你必须把你的视图和你的控件分开!MVC! 人们强烈地不鼓励UIViewinheritance,或者将应用逻辑放在UIView子类中。

但是我仍然决定去做(子类UIView)。 我已经围绕这个块几次了(但对于iPhone SDK / UIKit来说还是相当新的),而且我对devise非常敏感,这很丑陋,可能会导致问题,我坦率地看不到问题与UIView子类和添加sockets和操作。 实际上,将可重用的小部件作为UIView的一个子类,而不是基于UIViewController的部件有很多优点:

  • 您可以在客户视图的界面构build器布局中放置一个widget类的直接实例,并且在从xib加载客户类时,widget视图的UIView框架将被正确初始化。 这对我来说比在客户中放置一个“占位符视图”要干净得多,然后以编程方式实例化小部件模块,并将小部件的框架设置为占位符的框架,然后交换小部件视图的占位符视图。

  • 您可以创build一个干净而简单的xib文件来在界面构build器中布置小部件的组件。 文件的所有者被设置为您的小部件类。 nib文件包含所有GUI布局的根(vanilla)UIView,然后在小部件的awakeFromNib:方法中,将此nib视图作为子视图添加到小部件本身。 如果有必要的话,任何帧大小调整都可以在这里处理,完全在小部件代码中。 像这样:

 - (void) awakeFromNib { [[NSBundle mainBundle] loadNibNamed:@"MyWidgetView" owner:self options:nil]; [self addSubview:self.view]; } 
  • 小部件通过在自己的awakeFromNib方法中使用NSBundle的loadNibNamed:owner:options方法来自我初始化其用户界面,所以小部件与客户类的整合是干净的:只需将UIView放入IB的客户视图中,将UIView类更改为MyWidgetView,在客户的init或viewDidLoad中,使用自己编写的小部件的API(setScore:setTeamImage:等)设置窗口小部件显示的默认值。

在我看来,在这样一个可重用的“小部件”,并使用UIViewController的子类UIView之间的结果代码之间基本上没有区别。 在这两种情况下.h文件都包含窗口小部件类成员,出口,动作声明和特殊的API声明。 在这两种情况下,.m文件都包含操作实现和特殊的API实现。 视图是从控制分离 – 视图是在xib文件!

所以,总之,这就是我打包复杂的可重用视图小部件的方法:

  • 使小部件类成为UIView的一个子类
  • 通过拖动工具库中的UIView,并将类更改为“MyWidgetClass”,从而在您的应用程序的其他视图中实例化小部件。
  • 在一个根香草UIView内的笔尖devise你的小部件在UI中的用户界面。
  • 在小部件类的awakeFromNib:方法中,从xib加载小部件的UI,并执行[self addSubview:self.theXibRootView]

  • 像UIViewController一样对小部件进行编码:sockets,操作,特殊的API

我有兴趣听到其他用于创build可重用小部件的结构体系,以及这种方法的优点是什么。

如果小部件需要将事件报告给客户类,那么只需使用委托协议,客户将小部件的委托设置为self,就像在UIViewController中一样。