有一段时间我脑子里已经陷入了这种困境。 UITableView中的单元格本质上是一个视图,因此UITableViewCell的类应该处理与视图相关的事物(即表示方法,布局等),并且内部没有业务逻辑(通常需要处理控制器)。但是由于我们没有每个单元的控制器而只有整个表的控制器,所以我很难弄清楚在哪里放置我的单元逻辑。将它放在单元格本身会破坏MVC,但是将它放在表控制器中会使得很难确定调用该方法的单元格(如果视图是基于动作的,我更喜欢为我的发送者编写子类,所以我可以添加属性来帮助我确定这是什么观点)。
例如我有一个单元格,该单元格内部有一个UIButton,当按下按钮时会出现UIPopover。现在我在哪里放置popover演示代码(演示文稿出现在一个特定的单元格中,因此我必须知道从哪个单元格调用它。)
我想知道其他人在这种情况下做了什么以及他们的最佳做法是什么。
答案 0 :(得分:3)
如果你把popover的演示文稿放在单元格中,那么它是最好的选择。为什么?,因为这不是逻辑,这是视图相关的事情,因为执行此操作的按钮位于您的单元格内,然后代码应该在您的单元格内(或者您可以将消息(委托)发送到您的viewController以显示)。
然后是什么逻辑?逻辑是例如:计算,日期操作,向服务器发送内容。所有这些都应该在我们可以称之为module
或manager
的另一个对象中。
控制器可以在所有这些对象(view
- model
)之间交换消息,但视图和模块应该相互分离。
更新: 您可能需要查看Single Responsibility原则
答案 1 :(得分:1)
通常情况下,View Controller会为您的单元格处理“填充”逻辑。单元格是您每次填写的收件人。
甚至在prepareForReuse:
的{{1}}中说:
tableView中的表视图委托:cellForRowAtIndexPath:应该在重用单元格时重置所有内容。
事实上,除了显示之外,你的单元格不应该包含任何逻辑。
如果您需要单元格中的逻辑按钮,则应该为您的子类UITableViewCell
设置一个委托(您创建一个协议),然后在UIViewController中保存单元格逻辑。
如果您的单元格是唯一的,我建议您将单元格定义为静态单元格(无重用标识符)。并与之建立强有力的联系。
答案 2 :(得分:1)
您可以继承UITableView
和UITableViewCell
。然后,为按钮添加委托方法。例如tableView:buttonWasPressedForCell:
& buttonWasPressedForCell:
。 tableView将符合单元格的委托并接收消息buttonWasPressedForCell:
。然后,tableView会将消息tableView:buttonWasPressedForCell:
发送给它的委托,在本例中是您的控制器。通过这种方式,您可以了解发送邮件的UITableView
和UITableViewCell
。
示例:强>
<强> ABCTableView.h 强>
@protocol ABCTableViewDelegate <NSObject, UITableViewDelegate>
// You may not need this delegate method in a different UIViewController.
// So, lets set it to optional.
@optional
// Instead of passing the cell you could pass the index path.
- (void)tableView:(ABCTableView *)tableView buttonWasPressedForCell:(ABCTableViewCell *)cell;
@end
@interface ABCTableView : UITableView
// Declare the delegate as an IBOutlet to enable use with IB.
@property (weak, nonatomic) IBOutlet id<ABCTableViewDelegate> delegate;
@end
<强> ABCTableView.m 强>
@implementation ABCTableView
@dynamic delegate;
- (void)buttonWasPressedForCell:(ABCTableViewCell *)cell
{
// Check if the delegate responds to the selector since
// the method is optional.
if ([self.delegate respondsToSelector:@selector(tableView:buttonWasPressedForCell:)])
{
[self.delegate tableView:self buttonWasPressedForCell:cell];
}
}
@end
<强> ABCTableViewCell.h 强>
@protocol ABCTableViewCellDelegate;
@interface ABCTableViewCell : UITableViewCell
// Declare the delegate as an IBOutlet to enable use with IB.
@property (weak, nonatomic) IBOutlet id<ABCTableViewCellDelegate> delegate;
@end
@protocol ABCTableViewCellDelegate <NSObject>
// You may not need this delegate method in a different custom UITableView.
// So, lets set it to optional.
@optional
- (void)buttonWasPressedForCell:(ABCTableViewCell *)cell;
@end
<强> ABCTableViewCell.m 强>
@implementation ABCTableViewCell
- (IBAction)action:(id)sender
{
// Check if the delegate responds to the selector since
// the method is optional.
if ([self.delegate respondsToSelector:@selector(buttonWasPressedForCell:)])
{
[self.delegate buttonWasPressedForCell:self];
}
}
@end
注意:强>
当您在tableView:cellForRowAtIndexPath:
中出列单元格或使用Interface Builder添加单元格时,请确保将单元格的委托设置为tableView。
E.g。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
ABCTableViewCell *cell = (ABCTableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"Cell"];
cell.delegate = tableView;
return cell;
}
答案 3 :(得分:0)
通常对于像这样的任务,我将viewController作为委托分配给单元格(并为其定义一些协议)。另外,我保持对我填充单元格的对象的弱引用,所以在按钮的操作中,我将转发到委托(viewController)方法,如下所示:
- (void)actionOnCell:(UITableViewCell *)cell fromView:(UIView *)sender withItem:(id)sourceItem;
所以通过这种方式,我知道从哪里显示我的popover,以及哪些信息(适用于sourceItem
)显示在其中。
编辑此外,如果单元格上有多个控件以避免重复相似的方法,您可以在上面提到的函数中添加一个参数,并定义所有可能操作的枚举
答案 4 :(得分:0)
为单元格创建操作处理程序和数据源。让您的数据源符合数据源协议(View Model)。然后,细胞甚至不需要知道数据模型。
在界面中:TableViewCell
@property (nonatomic, weak) id <SomeTableViewCellActionHandler> actionHandler;
@protocol SomeTableViewCellActionHandler <NSObject>
- (void)cell:(SomeTableViewCell *)cell didReceiveStartButtonAction:(UIButton *)button;
- (void)cell:(SomeTableViewCell *)cell didReceivePauseButtonAction:(UIButton *)button;
- (void)cell:(SomeTableViewCell *)cell didReceiveClearButtonAction:(UIButton *)button;
@end
实施
- (void)prepareActionsForControls
{
[self.startButton addTarget:self action:@selector(handleStartButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[self.pauseButton addTarget:self action:@selector(handlePauseButtonAction:) forControlEvents:UIControlEventTouchUpInside];
[self.clearButton addTarget:self action:@selector(handleClearButtonAction:) forControlEvents:UIControlEventTouchUpInside];
}
- (void)handleStartButtonAction:(id)sender
{
[self.actionHandler cell:self didReceiveStartButtonAction:sender];
}
- (void)handlePauseButtonAction:(id)sender
{
[self.actionHandler cell:self didReceivePauseButtonAction:sender];
}
- (void)handleClearButtonAction:(id)sender
{
[self.actionHandler cell:self didReceiveClearButtonAction:sender];
}
在View Controller中创建单元格时 创建一个符合MyTableViewCellActionHandler协议的动作处理程序,如果需要进行演示,则将动作处理程序传递给View Controller。
cell.actionHandler = self.tableViewCellActionHandler;
您还可以为您的单元格提供数据源并传入视图模型。 (MVVM)这将允许您仅在单元格中保留演示文稿代码,并将所有业务逻辑保留在其所属的位置。关注点分离。