在故事板中,如何制作自定义单元格以供多个控制器使用?

我正在尝试在我正在处理的应用中使用故事板。 在应用程序中有列表用户 ,每个包含另一个(列表的成员,用户拥有的列表)的集合。 所以,相应地,我有ListCellUserCell类。 目标是让这些应用程序在整个应用程序中重用(即,在我的任何桌面视图控制器中)。

这就是我遇到问题的地方。

如何在故事板中创建可在任何视图控制器中重新使用的自定义表格单元格?

以下是我迄今尝试的具体事情。

  • 在控制器#1中,添加一个原型单元格,将该类设置为我的UITableViewCell子类,设置重用标识,添加标签并将它们连接到该类的插座。 在控制器#2中,添加了一个空的原型单元格,将它设置为相同的类并重新使用id。 运行时,单元显示在控制器#2中时,标签不会出现。 在控制器#1中正常工作。

  • 在不同的NIB中设计每种细胞类型,并连接到相应的细胞类别。 在故事板中,添加了一个空的原型单元格并设置其类并重用了id来引用我的单元格类。 在控制器的viewDidLoad方法中,为重用标识注册了这些NIB文件。 显示时,两个控制器中的单元格都像原型一样是空的。

  • 保持两个控制器中的原型为空并设置类,并将id重用到我的单元类。 用代码完全构造单元的UI。 细胞在所有控制器中完美工作。

  • 在第二种情况下,我怀疑原型总是覆盖NIB,如果我杀死了原型单元,注册我的NIB以获得重用ID将会起作用。 但之后我无法将细胞中的细胞设置到其他框架上,这实际上是使用故事板的关键。

    在一天结束时,我需要两件事情:在故事板中连接基于tableview的流程,并以可视方式而非代码方式定义单元格布局。 到目前为止,我看不出如何获得这两个。


    据我了解,你想要:

  • 在IB中设计一个单元格,可用于多个故事板场景。
  • 根据单元格所处的场景,从该单元配置独特的故事板段落。
  • 不幸的是,目前没有办法做到这一点。 要理解为什么以前的尝试不起作用,您需要更多地了解故事板和原型表格视图单元格的工作原理。 (如果你不在意为什么这些其他尝试不起作用,请随时离开,除了建议你提交错误外,我没有为你提供任何魔术解决方法。)

    故事板本质上不仅仅是一组.xib文件。 当你加载一个表格视图控制器,其中有一些原型单元格不在故事板中时,会发生以下情况:

  • 每个原型单元实际上都是它自己的嵌入式迷你笔尖。 所以当表视图控制器加载时,它会遍历每个原型单元的-[UITableView registerNib:forCellReuseIdentifier:]和调用-[UITableView registerNib:forCellReuseIdentifier:]
  • 表格视图向控制器询问单元。
  • 你可能会调用-[UITableView dequeueReusableCellWithIdentifier:]
  • 当您请求具有给定重复使用标识符的单元格时,它会检查是否注册了笔尖。 如果有,它实例化该单元的一个实例。 这包括以下步骤:

  • 按照单元格笔尖中的定义查看单元格的类。 调用[[CellClass alloc] initWithCoder:]
  • -initWithCoder:方法会遍历并添加子视图并设置在nib中定义的属性。 ( IBOutlet很可能会在这里连接,虽然我没有测试过;它可能发生在-awakeFromNib
  • 不管你想要怎样配置你的单元格。

  • 这里要注意的重要一点是细胞的类别和细胞的视觉外观之间存在区别。 您可以创建两个相同类的独立原型单元格,但其子视图布局完全不同。 事实上,如果您使用默认的UITableViewCell样式,这正是发生的情况。 例如,“Default”样式和“Subtitle”样式都由相同的UITableViewCell类表示。

    这很重要 :单元格的与特定视图层次结构之间没有一对一的关联。 视图层次结构完全由原型单元格中的内容决定,该原型单元格已在此特定控制器中注册。

    请注意,单元格的重用标识符未在某个全局单元格药房中注册。 重用标识符仅用于单个UITableView实例的上下文中。


    给出这些信息,让我们看看你上面的尝试发生了什么。

    在控制器#1中,添加一个原型单元格,将该类设置为我的UITableViewCell子类,设置重用标识,添加标签并将它们连接到该类的插座。 在控制器#2中,添加了一个空的原型单元格,将它设置为相同的类并重新使用id。 运行时,单元显示在控制器#2中时,标签不会出现。 在控制器#1中正常工作。

    这是预料之中的。 虽然两个单元格具有相同的类,但传递给控制器​​#2中的单元格的视图层次结构完全没有子视图。 所以你得到了一个空单元,这正是你在原型中所做的。

    在不同的NIB中设计每种细胞类型,并连接到相应的细胞类别。 在故事板中,添加了一个空的原型单元格并设置其类并重用了id来引用我的单元格类。 在控制器的viewDidLoad方法中,为重用标识注册了这些NIB文件。 显示时,两个控制器中的单元格都像原型一样是空的。

    再次,这是预期的。 重用标识符不在故事板场景或笔尖之间共享,因此所有这些不同的单元具有相同的重用标识符这一事实是毫无意义的。 从tableview中返回的单元格将具有与故事板的该场景中的原型单元格相匹配的外观。

    虽然这个解决方案很接近。 正如你所指出的,你可以通过编程的方式调用-[UITableView registerNib:forCellReuseIdentifier:] ,传递包含单元格的UINib ,然后你会找回同一个单元格。 (这不是因为原型“凌驾”了笔尖;你只是没有使用tableview注册笔尖,所以它仍然在看故事板中嵌入的笔尖)。不幸的是,这种方法存在缺陷 - 没有办法将故事板游戏连接到独立笔尖的单元格中。

    保持两个控制器中的原型为空并设置类,并将id重用到我的单元类。 用代码完全构造单元的UI。 细胞在所有控制器中完美工作。

    自然。 希望这并不令人惊讶。


    所以,这就是为什么它不起作用。 你可以在单独的笔尖中设计你的单元格并在多个故事板场景中使用它们; 你现在无法将故事板游戏连接到这些单元格。 不过,希望你在阅读过程中学到了一些东西。


    尽管BJ荷马很好的回答,但我觉得我有一个解决方案。 至于我的测试,它的工作。

    概念:为xib单元创建一个自定义类。 在那里,你可以等待一个触摸事件,并以编程方式执行segue。 现在我们需要的是参考执行Segue的控制器。 我的解决方案是将其设置在tableView:cellForRowAtIndexPath:

    我有一个DetailedTaskCell.xib包含一个表格单元格,我想在多个表格视图中使用:

    DetailedTaskCell.xib

    该单元有一个自定义类TaskGuessTableCell

    在这里输入图像描述

    这是魔术发生的地方。

    // TaskGuessTableCell.h
    #import <Foundation/Foundation.h>
    
    @interface TaskGuessTableCell : UITableViewCell
    @property (nonatomic, weak) UIViewController *controller;
    @end
    
    // TashGuessTableCell.m
    #import "TaskGuessTableCell.h"
    
    @implementation TaskGuessTableCell
    
    @synthesize controller;
    
    - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
        NSIndexPath *path = [controller.tableView indexPathForCell:self];
        [controller.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
        [controller performSegueWithIdentifier:@"FinishedTask" sender:controller];
        [super touchesEnded:touches withEvent:event];
    }
    
    @end
    

    我有多个Segues,但它们都具有相同的名称: "FinishedTask" 。 如果您需要灵活一点,我建议添加另一个属性。

    ViewController看起来像这样:

    // LogbookViewController.m
    #import "LogbookViewController.h"
    #import "TaskGuessTableCell.h"
    
    @implementation LogbookViewController
    
    - (void)viewDidLoad
    {
        [super viewDidLoad]
    
        // register custom nib
        [self.tableView registerNib:[UINib nibWithNibName:@"DetailedTaskCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"DetailedTaskCell"];
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        TaskGuessTableCell *cell;
    
        cell = [tableView dequeueReusableCellWithIdentifier:@"DetailedTaskCell"];
        cell.controller = self; // <-- the line that matters
        // if you added the seque property to the cell class, set that one here
        // cell.segue = @"TheSegueYouNeedToTrigger";
        cell.taskTitle.text  = [entry title];
        // set other outlet values etc. ...
    
        return cell;
    }
    
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
    {
        if([[segue identifier] isEqualToString:@"FinishedTask"])
        {
            // do what you have to do, as usual
        }
    
    }
    
    @end
    

    有可能有更多优雅的方式来实现相同,但 - 它的工作原理! :)


    我正在寻找这个,我发现理查德维纳布尔的答案。 这个对我有用。

    iOS 5在UITableView上包含一个新方法:registerNib:forCellReuseIdentifier:

    要使用它,请将UITableViewCell放入笔尖。 它必须是笔尖中唯一的根对象。

    你可以在加载你的tableView之后注册nib,然后当你调用dequeueReusableCellWithIdentifier时:使用单元标识符将它从nib中拉出来,就像你使用了Storyboard原型单元一样。

    链接地址: http://www.djcxy.com/p/34783.html

    上一篇: In a storyboard, how do I make a custom cell for use with multiple controllers?

    下一篇: App Browser not loading?