在故事板中,如何制作用于多个控制器的自定义单元?

Cli*_*f W 216 objective-c storyboard uitableview ios

我正在尝试在我正在使用的应用程序中使用故事板.在应用程序中有ListsUsers,每个都包含其他列表(列表成员,用户拥有的列表).所以,相应地,我有ListCellUserCell课.目标是让它们在整个应用程序中可重复使用(即,在我的任何tableview控制器中).

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

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

以下是我迄今为止尝试过的具体内容.

  • 在Controller#1中,添加了一个原型单元格,将类设置为我的UITableViewCell子类,设置重用ID,添加标签并将它们连接到类的出口.在Controller#2中,添加了一个空的原型单元格,将其设置为同一个类并重复使用id.当它运行时,当控制器#2中显示单元格时,标签永远不会出现.在Controller#1中正常工作.

  • 在不同的NIB中设计每种细胞类型,并连接到适当的细胞类.在storyboard中,添加了一个空的原型单元格并设置其类并重用id来引用我的单元类.在控制器的viewDidLoad方法中,为重用ID注册了那些NIB文件.如图所示,两个控制器中的单元格都像原型一样空.

  • 将两个控制器中的原型保持为空并设置类并将id重用于我的单元类.完全用代码构建单元格的UI.细胞在所有控制器中都能完美运

在第二种情况下,我怀疑原型总是覆盖NIB,如果我杀死原型单元,注册我的NIB用于重用ID将起作用.但后来我无法设置从单元格到其他框架的segue,这实际上是使用故事板的全部要点.

在一天结束时,我想要两件事:在故事板中连接基于tableview的流,并在视觉上而不是在代码中定义单元格布局.到目前为止,我无法看到如何获得这两者.

BJ *_*mer 205

据我了解,你想:

  1. 在IB中设计一个单元格,可用于多个故事板场景.
  2. 根据单元格所在的场景,从该单元格配置唯一的故事板segue.

不幸的是,目前没有办法做到这一点.要了解您之前的尝试无效的原因,您需要了解有关故事板和原型表视图单元格如何工作的更多信息.(如果你不关心为什么这些其他尝试都不起作用,请随时离开.除了建议你提交bug之外,我没有为你提供任何神奇的解决方法.)

故事板本质上只不过是.xib文件的集合.当您从故事板中加载一个具有一些原型单元格的表视图控制器时,会发生以下情况:

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

    1. 查看单元格的类,如单元格的笔尖中所定义.打电话[[CellClass alloc] initWithCoder:].
    2. -initWithCoder:方法遍历并添加子视图并设置在nib中定义的属性.(IBOutlet也许我可能会联系到这里,虽然我没有测试过;它可能会发生在-awakeFromNib)
  • 您可以根据需要配置单元格.

这里要注意的重要一点是细胞的类别和细胞的视觉外观之间存在区别.您可以创建同一类的两个单独的原型单元格,但它们的子视图完全不同.事实上,如果你使用默认UITableViewCell样式,这正是正在发生的事情.例如,"默认"样式和"字幕"样式都由同一个UITableViewCell类表示.

这很重要:单元格的与特定视图层次结构没有一对一的关联.视图层次结构完全取决于在此特定控制器中注册的原型单元格中的内容.

另请注意,单元的重用标识符未在某个全局单元诊所中注册.重用标识符仅在单个UITableView实例的上下文中使用.


鉴于此信息,让我们看一下上述尝试中发生的情况.

在Controller#1中,添加了一个原型单元,将类设置为我的UITableViewCell子类,设置重用ID,添加标签并将它们连接到类的出口.在Controller#2中,添加了一个空的原型单元格,将其设置为同一个类并重复使用id.当它运行时,当控制器#2中显示单元格时,标签永远不会出现.在Controller#1中正常工作.

这是预料之中的.虽然两个单元格具有相同的类,但传递给Controller#2中的单元格的视图层次结构完全没有子视图.所以你有一个空单元格,这正是你在原型中放入的单元格.

在不同的NIB中设计每种细胞类型,并连接到适当的细胞类.在storyboard中,添加了一个空的原型单元格并设置其类并重用id来引用我的单元类.在控制器的viewDidLoad方法中,为重用ID注册了那些NIB文件.如图所示,两个控制器中的单元格都像原型一样空.

再次,这是预期的.重用标识符不故事板的场景或可可豆粒之间共享,这样的事实,所有这些不同的细胞具有相同的重用标识符是毫无意义的.从tableview返回的单元格将具有与故事板场景中的原型单元格匹配的外观.

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

将两个控制器中的原型保持为空并设置类并将id重用于我的单元类.完全用代码构建单元格的UI.细胞在所有控制器中都能完美运

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


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

  • @RichApodaca我在答案中提到了这个解决方案.但它不在故事板中; 它在一个单独的笔尖中.因此,你无法连接segues或做其他故事板.因此,它并没有完全解决最初的问题. (7认同)

eri*_*ert 58

尽管BJ Homer给出了很好的答案,但我觉得我有一个解决方案.就我的测试而言,它有效.

概念:为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
Run Code Online (Sandbox Code Playgroud)

我有多个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
Run Code Online (Sandbox Code Playgroud)

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

  • 请注意,使用此解决方案,每次触摸在单元格内部结束时,您都会触发segue.这包括如果您只是滚动单元格,而不是实际尝试选择它.你可能有更好的运气覆盖单元格上的`-setSelected:`,并且只有在从'NO`转换为'YES`时才触发segue. (9认同)

Odr*_*kir 16

我正在寻找这个,我找到了Richard Venable的答案.这个对我有用.

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

要使用它,请在笔尖中放置UITableViewCell.它必须是笔尖中唯一的根对象.

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


jrt*_*ton 10

BJ荷马给出了一个很好的解释.

从实际的角度来看,我补充说,鉴于你不能将单元格作为xib并连接segue,最好选择的是将单元格作为xib - 过渡比单元格布局和多个位置的属性更容易维护,无论如何,你的segue可能与你的不同控制器不同.您可以直接从表视图控制器定义segue到下一个控制器,并在代码中执行它..

另外需要注意的是,将您的单元格作为单独的xib文件阻止您将任何操作等直接连接到表视图控制器(我还没有解决这个问题 - 无论如何 - 您无法将文件所有者定义为有意义的任何内容).我正在解决这个问题,方法是定义一个单元的表视图控制器应该符合的协议,并将控制器作为一个弱属性添加,类似于委托,在cellForRowAtIndexPath中.


Kun*_*mar 10

斯威夫特3

BJ荷马给出了一个很好的解释,它帮助我理解这个概念.To make a custom cell reusable in storyboard,可以在我们必须mix the Storyboard and xib接近的任何TableViewController中使用.假设我们有一个名为的单元格CustomCell将用于TableViewControllerOneTableViewControllerTwo.我正在分步进行.
1.文件>新建>单击文件>选择Cocoa Touch类>单击下一步>为您的类命名(例如CustomCell)>选择子类作为UITableVieCell>勾选也创建XIB文件复选框,然后按下一步.
2.根据需要自定义单元格,并在属性检查器中为单元格设置标识符,这里我们将设置为CellIdentifier.此标识符将在ViewController中用于标识和重用Cell.
3.现在我们只需要register this cell在ViewController中使用viewDidLoad.无需任何初始化方法.
4.现在我们可以在任何tableView中使用这个自定义单元格.

在TableViewControllerOne中

let reuseIdentifier = "CellIdentifier"

override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: reuseIdentifier)
} 

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier:reuseIdentifier, for: indexPath) as! CustomCell
    return cell!
}
Run Code Online (Sandbox Code Playgroud)


Joã*_*nes 5

我找到了一种方法来加载相同VC的单元格,而不是测试segue.这可能是在单独的笔尖中创建单元格的解决方法

假设您有一个VC和2个表,并且您想在故事板中设计一个单元格并在两个表中使用它.

(例如:带有UISearchController的表和搜索字段,其中包含结果表,并且您希望在两者中使用相同的Cell)

当控制器要求单元格执行此操作时:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * identifier = @"CELL_ID";

    ContactsCell *cell = [self.YOURTABLEVIEW dequeueReusableCellWithIdentifier:identifier];
  // Ignore the "tableView" argument
}
Run Code Online (Sandbox Code Playgroud)

在这里,您可以从故事板中获取您的单元格