在Interface Builder中使用id 作为文件所有者?

我有一个自定义的UITableViewCell ,我使用instantiateWithOwner:(id)owner options:(NSDictionary *)options从一个nib instantiateWithOwner:(id)owner options:(NSDictionary *)options 。 实例化nib时,我将其保存到我的视图控制器中定义的IBOutlet,该控制器在.xib文件中设置为文件的所有者。 一切都很好。

我现在遇到了在多个视图控制器中使用此自定义单元的需要。 我希望我可以定义一个协议(例如CustomCellOwner),多个视图控制器可以实现。 该协议将简单地定义用于在实例化时引用该单元的IBOutlet。

理想情况下,我想将“文件所有者”设置为:

 id  

在Interface Builder中。

但是,Interface Builder似乎只允许您将文件所有者设置为已知类,而不是实现协议的id?

有没有办法做到这一点? 或者,一种更简单的方法来解决这个问题?

谢谢!

这不是您要求的解决方案,但您可以创建一个UIViewController子类,为每个需要使用nib的视图控制器创建子类。 就像是:

 @interface CustomCellOwnerViewController : UIViewController @property (nonatomic, strong) IBOutlet UIButton *someButton; -(IBAction)doSomething; @end 

然后使用它作为每个的基类:

 @interface FirstView : CustomCellOwnerViewController 

然后你可以简单地将File's Owner设置为CustomCellOwnerViewController而没有任何问题。

只是一个想法。

我今天碰到了这个并没有找到一个好的解决方案。 然而,我做了它,所以它似乎工作正常。 这绝对感觉像是一个黑客。

首先,我创建了一个“fakeOwner”类,如下所示:

 @interface fakeOwner : NSObject @property (nonatomic, assign) IBOutlet MyBaseCell* itemTableCell; @end @implementation fakeOwner @synthesize itemTableCell; @end 

然后我将对象的所有者在XIB中设置为fakeOwner并连接sockets。 然后对于想要使用这些单元格的每个控制器,我添加相同的属性并创建如下所示的类:

  [[NSBundle mainBundle] loadNibNamed:@"MyBaseCell" owner:self options:nil]; MyBaseCell* itemCell = self.itemTableCell; self.itemTableCell = nil; 

由于fakeOwner和我的控制器具有相同的IBOutlet,因此使用控制器作为所有者加载单元会导致连接发生,即使这不是在XIB中明确设置的。

如果内存管理目前是正确的(我认为没关系),不是100%,但除此之外它似乎工作得很好。 我希望看到一个更好的方法来做到这一点。

制作一个假的主人会工作; 然而,这种解决方案可能是脆弱的,也是不可扩展的。 从某种意义上说,细胞拥有自己,但即使这在技术上也是不正确的。 事实是UITableViewCell没有所有者。

实现自定义表视图单元的正确方法是首先创建UITableViewCell的自定义子类。 在本课程中,您将为单元格定义所有IBOutlet等。 以下是头文件的示例:

 @interface RBPersonCell : UITableViewCell @property (nonatomic, strong) IBOutlet UILabel * nameLabel; @property (nonatomic, strong) IBOutlet UILabel * ageLabel; - (void)setupWithPerson:(Person *)person; @end 

从那里,我有一个方便的方法,如果需要,从笔尖创建单元格:

 + (id)cellForTableView:(UITableView *)tableView reuseIdentifier:(NSString *)reuseID fromNib:(UINib *)nib { if (!reuseID) reuseID = [self cellIdentifier]; id cell = [tableView dequeueReusableCellWithIdentifier:reuseID]; if (!cell) { NSArray * nibObjects = [nib instantiateWithOwner:nil options:nil]; // Sanity check. NSAssert2(([nibObjects count] > 0) && [[nibObjects objectAtIndex:0] isKindOfClass:[self class]], @"Nib '%@' does not appear to contain a valid %@", [self nibName], NSStringFromClass([self class])); cell = [nibObjects objectAtIndex:0]; } return cell; } 

此方法封装了所有创建代码,因此我无需查看或重写它。 它假定自定义单元格是nib中的第一个根视图。 这是一个相当安全的假设,因为您应该只将自定义单元格作为根视图。

有了所有这些代码,您就可以在Interface Builder中工作了。 首先需要在身份检查中设置自定义类。 接下来,不要忘记设置您的小区标识符。 为方便起见,最好使用自定义类的名称。 拖动连接时,不要将它们拖到文件所有者,而是将连接拖到自定义单元格本身。

我所了解的关于自定义表格视图单元格的大部分内容来自iOS Recipes食谱15-16。 这是一本免费的摘录,直接来自The Pragmatic Bookshelf 。 您可以查看该书以获取更多详细信息。

编辑:

我终于开始寻找我的RBSmartTableViewCell类了。 你可以在我的GitHub上找到它。 您应该发现这个类比直接来自iOS Recipes的代码更有用,因为我的类将所有单元格视为相同,无论它们是使用XIB,UIStoryboard还是代码构造。 该回购还包括工作样本。

在iOS 5.0中,现在在UITableView上有registerNib:forCellReuseIdentifier:方法,我认为它试图解决类似的问题。

从文档:

当您使用表视图注册nib对象并稍后调用dequeueReusableCellWithIdentifier:方法时,传入已注册的标识符,表视图将从nib对象中实例化该单元(如果它尚未在重用队列中)。

根据您的要求,这可能是一种替代方法。

另一种选择可能是创建一个轻量级的“工厂”对象来处理为您创建单元格。 此对象将是界面构建器中的rootObject ,其中rootObject出口设置正确。

 @interface NibLoader : NSObject @property (nonatomic, strong) UINib * nib; @property (nonatomic, strong) IBOutlet id rootObject; - (id)initWithNibName:(NSString *)name bundle:(NSBundle *)bundleOrNil; - (id)instantiateRootObject; @end @implementation NibLoader @synthesize nib, rootObject; - (id)initWithNibName:(NSString *)name bundle:(NSBundle *)bundleOrNil { self = [super init]; if (self) { self.nib = [UINib nibWithNibName:name bundle:bundleOrNil]; } return self; } - (id)instantiateRootObject { self.rootObject = nil; [self.nib instantiateWithOwner:self options:nil]; NSAssert(self.rootObject != nil, @"NibLoader: Nib did not set rootObject."); return self.rootObject; } @end 

然后在视图控制器中:

 NibLoader *customCellLoader = [[NibLoader alloc] initWithNibName:@"CustomCell" bundle:nil]; self.customCell = customCellLoader.instantiateRootObject; 

我更喜欢显式设置根对象而不是搜索从instantiateWithOwner:options:返回的数组instantiateWithOwner:options:因为我知道此数组中对象的位置在过去已经改变。