【问题标题】:Using id<protocol> for file's owner in Interface Builder?在 Interface Builder 中使用 id<protocol> 作为文件的所有者?
【发布时间】:2012-02-20 21:25:35
【问题描述】:

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

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

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

id <CustomCellOwner>

在界面生成器中。

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

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

谢谢!

【问题讨论】:

  • 完整的旁注,你能指出一些你如何做到这一点的指南或示例,因为它听起来很棒,目前我必须循环浏览笔尖才能找到合适的视图以在我的自定义单元格。这并不理想。听起来你有一个很好的解决方案。
  • 您能否详细说明“它似乎只允许您将文件的所有者设置为已知类,而不是实现协议的 id?”是界面生成器的意思吗?
  • @rooftop 是的,在这种情况下,“it”指的是 Interface Builder。编辑/配置文件的所有者对象时,有一个“类”选项,可让您选择文件所有者的类。 IB 中的那个框不允许我输入 id 作为类型,我想知道这是否可能。谢谢!

标签: objective-c ios interface-builder


【解决方案1】:

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

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

然后将其用作每个的基类:

@interface FirstView : CustomCellOwnerViewController

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

只是一个想法。

【讨论】:

    【解决方案2】:

    我今天遇到了这个问题,但没有找到好的解决方案。但是,我确实对其进行了破解,以使其似乎可以正常工作。不过,这绝对是一种黑客行为。

    首先我创建了一个像这样的“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%(我认为没问题),但除此之外,它似乎工作得很好。不过,我希望看到更好的方法。

    【讨论】:

      【解决方案3】:

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

      实现自定义表格视图单元格的正确方法是首先创建 UITableViewCell 的自定义子类。在此类中,您将为单元定义所有 IBOutlets 等。这是一个头文件的示例:

      @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 recipes 15-16。这是直接来自The Pragmatic Bookshelffree extract。您可以查看该书以了解更多详细信息。

      编辑:

      我终于开始开源我的RBSmartTableViewCell 课程。你可以在我的GitHub 上找到它。你应该会发现这个类比直接来自 iOS 食谱的代码更有用,因为我的类对所有单元格一视同仁,无论它们是使用 XIB、UIStoryboard 还是代码构造的。此 repo 还包括工作示例。

      【讨论】:

        【解决方案4】:

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

        来自文档:

        当您向表格视图注册一个 nib 对象并随后调用 dequeueReusableCellWithIdentifier: 方法并传入注册的标识符时,如果该 nib 对象尚未在重用队列中,表格视图会从该 nib 对象实例化该单元格。

        这可能是一种替代方法,具体取决于您的要求。

        【讨论】:

          【解决方案5】:

          另一种选择可能是创建一个轻量级的“工厂”对象,为您处理单元格的创建。该对象将是接口生成器中的FilesOwner,并适当设置了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: 返回的数组,因为我知道该数组中对象的位置在过去发生了变化。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-03-26
            • 2012-09-28
            • 2010-09-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-04-19
            • 1970-01-01
            相关资源
            最近更新 更多