在故事板中,如何制作一个自定义的单元格以供多个控制器使用?
我正在尝试在正在处理的应用程序中使用故事板。 在应用程序中有列表和用户 ,每个包含另一个(列表的成员,用户拥有的列表)的集合。 所以,相应地,我有ListCell
和UserCell
类。 目标是让这些应用程序可重用(即在我的任何一个桌面视图控制器中)。
这就是我遇到问题的地方。
如何在故事板中创build可在任何视图控制器中重新使用的自定义表格单元格?
以下是我迄今尝试的具体事情。
-
在控制器#1中,添加一个原型单元格,将类设置为我的
UITableViewCell
子类,设置重用标识,添加标签并将它们连接到类的sockets。 在控制器#2中,添加一个空的原型单元格,将其设置为相同的类,并重复使用id。 运行时,单元格显示在控制器#2中时,标签不会出现。 在控制器#1中正常工作。 -
在不同的NIB中devise每种细胞types,并连接到相应的细胞类别。 在故事板中,添加了一个空的原型单元格,并设置其类和重用id来引用我的单元格类。 在控制器的
viewDidLoad
方法中,为重用标识注册这些NIB文件。 如图所示,两个控制器中的单元都像原型一样是空的。 -
保持两个控制器中的原型为空并设置类,并将id重用到我的单元类。 用代码完全构造单元的UI。 细胞在所有控制器中都能正常工
在第二种情况下,我怀疑原型总是覆盖NIB,如果我杀死了原型单元,注册我的NIB重用ID将工作。 但是,我不能将细胞从细胞设置到其他的框架,这实际上是使用故事板的全部点。
在一天结束的时候,我想要两件事情:在故事板中连接基于tableview的stream程,并以可视方式定义单元布局而不是代码。 到目前为止,我看不出如何得到这两个。
据我了解,你想:
- 在IB中devise一个单元格,可用于多个故事板场景。
- 根据单元格所处的场景,从该单元格configuration独特的故事板段落。
不幸的是,目前没有办法做到这一点。 要理解为什么以前的尝试不起作用,您需要了解更多关于故事板和原型表格视图单元格的工作方式。 (如果你不在乎为什么这些其他的尝试不起作用,请随时离开,除了暗示你提交了一个bug之外,我没有给你任何魔术般的解决办法。
故事板本质上不过是一组.xib文件。 当你加载一个表格视图控制器,有一些原型单元格的故事板,这里发生了什么:
- 每个原型单元实际上都是自己的embedded式迷你笔尖。 所以,当表视图控制器加载时,它运行通过每个原型单元的笔尖和调用
-[UITableView registerNib:forCellReuseIdentifier:]
。 - 表格视图询问控制器的单元格。
- 你可能会调用
-[UITableView dequeueReusableCellWithIdentifier:]
-
当你请求一个给定的重用标识符的单元格时,它会检查它是否有一个注册的nib。 如果是,则实例化该单元的一个实例。 这是由以下步骤组成的:
- 按照单元格的笔尖定义,查看单元格的类。 调用
[[CellClass alloc] initWithCoder:]
。 -
-initWithCoder:
方法通过并添加子视图并设置在nib中定义的属性。 (IBOutlet
很可能会被挂在这里,虽然我没有testing过;它可能发生在-awakeFromNib
)
- 按照单元格的笔尖定义,查看单元格的类。 调用
-
你可以根据自己的需要configuration你的单元
这里需要注意的是,细胞的类别和细胞的视觉外观是有区别的。 您可以创build两个相同类的独立原型单元格,但其子视图布局完全不同。 事实上,如果使用默认的UITableViewCell
样式,这正是发生的事情。 例如,“Default”风格和“Subtitle”风格都由相同的UITableViewCell
类表示。
这很重要 :单元格的类与特定的视图层次结构没有一对一的关联。 视图层次结构完全取决于在此特定控制器中注册的原型单元中的内容。
请注意,单元格的重用标识符在某个全局单元格药房中没有注册。 重用标识符仅用于单个UITableView
实例的上下文中。
给出这些信息,让我们看看你上面的尝试发生了什么。
在控制器#1中,添加一个原型单元格,将类设置为我的UITableViewCell子类,设置重用标识,添加标签并将它们连接到类的sockets。 在控制器#2中,添加一个空的原型单元格,将其设置为相同的类,并重复使用id。 运行时,单元格显示在控制器#2中时,标签不会出现。 在控制器#1中正常工作。
这是预料之中的。 虽然两个单元格具有相同的类,但是传递给控制器#2中的单元格的视图层次结构完全没有子视图。 所以你得到了一个空单元,这正是你在原型中所做的。
在不同的NIB中devise每种细胞types,并连接到相应的细胞类别。 在故事板中,添加了一个空的原型单元格,并设置其类和重用id来引用我的单元格类。 在控制器的viewDidLoad方法中,为重用标识注册这些NIB文件。 如图所示,两个控制器中的单元都像原型一样是空的。
再一次,这是预料之中的。 重复标识符不是在故事板场景或笔尖之间共享的,所以所有这些不同的单元具有相同的重用标识符的事实是没有意义的。 从tableview中返回的单元格将具有与故事板的该场景中的原型单元格相匹配的外观。
虽然这个解决scheme很接近。 正如你所提到的,你可以通过编程调用-[UITableView registerNib:forCellReuseIdentifier:]
,传递包含单元格的UINib
,然后返回同一个单元格。 (这不是因为原型“凌驾”了笔尖,而只是没有在桌面上注册笔尖,所以它仍然在看故事板中embedded的笔尖)。不幸的是,这种方法存在缺陷 – 没有办法将故事板游戏连接到独立笔尖的单元格中。
保持两个控制器中的原型为空并设置类,并将id重用到我的单元类。 用代码完全构造单元的UI。 细胞在所有控制器中都能正常工
自然。 希望这是不足为奇的。
所以,这就是为什么它没有工作。 你可以在单独的笔尖中devise你的单元格,并在多个故事板场景中使用它们; 你现在还不能将故事板上的细节连接到这些单元格。 但是,希望在阅读过程中学到了一些东西。
尽pipeBJ荷马很好的回答,我觉得我有一个解决scheme。 至于我的testing,它的作品。
概念:为xib单元创build一个自定义类。 在那里你可以等待一个触摸事件,并以编程方式执行segue。 现在我们需要的是对控制器执行Segue的引用。 我的解决scheme是将其设置在tableView:cellForRowAtIndexPath:
例
我有一个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"
。 如果你需要在这里灵活,我build议添加另一个属性。
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原型单元格一样。
BJ荷马已经给出了一个很好的解释。
从实际的angular度来看,我补充说,既然你不能把单元格作为xibs并连接segues,最好的select就是将单元格作为xib – 转换要比多个单元格的单元格布局和属性更容易维护,无论如何,你的血统可能与你的不同控制器有所不同。 您可以直接从您的表视图控制器定义segue到下一个控制器,并在代码中执行它。 。
还有一点需要注意的是,把你的单元格作为一个单独的xib文件,可以防止你直接将任何操作等连接到表视图控制器(我还没有完成这项工作 – 无论如何 – 你不能将文件的所有者定义为任何有意义的)。 我正在解决这个问题,通过定义一个协议,单元格的表视图控制器预期符合,并添加控制器作为一个弱的属性,类似于委托,在cellForRowAtIndexPath。
Swift 3
BJ荷马给出了一个很好的解释,这有助于我理解这个概念。 为了make a custom cell reusable in storyboard
可以make a custom cell reusable in storyboard
,可以在任何TableViewController中使用,我们必须mix the Storyboard and xib
方法。 假设我们有一个名为CustomCell
的单元, CustomCell
在TableViewControllerOne
和TableViewControllerTwo
。 我正在逐步完成。
1.文件>新build>点击文件>selectcocoa触摸类>点击下一步>给你的类的名称(例如CustomCell
)>select子类作为UITableVieCell>勾选也创buildXIB文件checkbox,然后按下一步。
2.根据需要自定义单元格,并在属性检查器中为单元格设置标识符,这里我们将设置为CellIdentifier
。 这个标识符将被用在你的ViewController中来识别和重用Cell。
3.现在我们只需要在ViewController的viewDidLoad
register this cell
。 不需要任何初始化方法。
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! }
我find了一个方法来加载相同的VC单元格,没有testing赛格。 这可能是在单独的笔尖中创build单元格的解决方法
假设你有一个VC和两个表格,你想在故事板上devise一个单元格,并在两个表格中使用它。
(例如:一个表和一个带有UISearchController的search字段,其中有一个用于结果的表格,并且你想在两者中使用同一个Cell)
当控制器要求这个单元时:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString * identifier = @"CELL_ID"; ContactsCell *cell = [self.YOURTABLEVIEW dequeueReusableCellWithIdentifier:identifier]; // Ignore the "tableView" argument }
在这里你有你的细胞从故事板