在Interface Builder中使用id <protocol>作为文件所有者?</protocol>

时间:2012-02-20 21:25:35

标签: objective-c ios interface-builder

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

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

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

id <CustomCellOwner>
在Interface Builder中

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

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

谢谢!

5 个答案:

答案 0 :(得分:3)

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

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

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

@interface FirstView : CustomCellOwnerViewController

然后,您只需将File's Owner设置为CustomCellOwnerViewController即可。

只是一个想法。

答案 1 :(得分:1)

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

首先我创建了一个像这样的“fakeOwner”类:

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

@implementation fakeOwner
@synthesize itemTableCell;
@end

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

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

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

如果内存管理目前是正确的,那么不是100%(我认为没关系),但除此之外它似乎工作得很好。我很想看到更好的方法。

答案 2 :(得分:1)

制作一个假的主人会工作;然而,这样的解决方案可能是脆弱的和不可扩展的。从某种意义上说,细胞拥有自己,但即使这在技术上也是不正确的。事实是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;
}

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

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

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

修改

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

答案 3 :(得分:1)

在iOS 5.0中,现在在registerNib:forCellReuseIdentifier:上有UITableView方法,我相信它会尝试解决类似的问题。

来自文档:

  

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

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

答案 4 :(得分:1)

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

@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:返回的数组,因为我知道此数组中对象的位置在过去已经改变。