我在调用tableView:cellForIndexPath
期间修改了一个表视图单元格,但是在单元格滚动然后重新打开之前,修改不会出现。我意识到我可以修改.xib文件或者可以分配/初始化一个单元而不是调用dequeue
,但是我想了解发生了什么。
这些变化涉及掩盖细胞以实现圆角底角。如果我设置了角半径(即cell.layer.cornerRadius = ...
),这会使所有角落都圆,我就不会看到问题。
更新:
我最初没有发布代码,因为它是专有的,涉及很多复杂性,我认为在没有任何细节的情况下问题是合理的。然而,根据响应,这里是一个显示问题的简化片段,唯一的变化是实际的小区标识符。我还更新了描述/问题以简化方案。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TheCorrectCellIdentifier" forIndexPath:indexPath];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:cell.bounds byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(4., 4.)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = cell.bounds;
maskLayer.path = maskPath.CGPath;
cell.layer.masksToBounds = YES;
cell.layer.mask = maskLayer;
return cell;
}
答案 0 :(得分:26)
您的代码正常运行。但如果您仍然在cellForRowAtIndexPath:
中遇到角半径问题,请尝试willDisplayCell:
。我尝试了两种方法并且工作正常。
willDisplayCell:
告诉委托表视图是否要绘制一个单元格 特定行。
表视图仅将此消息发送给其委托 在它使用单元格绘制一行之前,从而允许代表进行 在显示单元格对象之前自定义它。这种方法给出了 委托有机会覆盖先前设置的基于状态的属性 通过表格视图,例如选择和背景颜色。之后 委托返回,表视图仅设置alpha和frame 属性,然后仅在向行或向外滑动动画时动画。 这是我的代码。
据我所知,Apple提供了两套不同的方法(协议),名为UITableViewDataSource
和UITableViewDelegate
来管理UITableView。
UITableViewDataSource :数据源为单元格提供实际数据以填写详细信息。
UITableViewDelegate :在另一端,委托负责提供有关tableview的可视布局的信息并处理用户交互。
因此,结论是DataSource主要处理表的内容(Data)和Delegate处理tableview组件的表示和交互。
让我们来处理TableView。
我们都非常个人使用UITableViewDataSource方法tableView:cellForRowAtIndexPath:
,这个方法负责创建或重用有效的UITableViewCell,并为每个索引路径提供适当的详细信息。我们倾向于在此进行单元布局配置和UI修改方法。
调用tableView:cellForRowAtIndexPath:
后,系统会进行布局配置,然后在屏幕上显示单元格之前调用tableView:willDisplayCell:forRowAtIndexPath:
方法。
因此我们可以使用此方法进行Tableview单元格的视图配置(可能是,Apple为我们提出了这种方法!!!)。
因此,我绝对倾向于使用tableView:cellForRowAtIndexPath:
为tableView:willDisplayCell:forRowAtIndexPath:
方法创建单元格和左单元格UI配置任务。
在willDisplayCell:
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath{
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:cell.bounds byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(10.0, 10.0)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = cell.bounds;
maskLayer.path = maskPath.CGPath;
cell.layer.masksToBounds = YES;
cell.layer.mask = maskLayer;
}
这是我的cellForRowAtIndexPath:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
cell.textLabel.text = [NSString stringWithFormat:@"Row : %ld ", (long)indexPath.section];
return cell;
}
输出:
答案 1 :(得分:6)
在viewDidAppear
或viewDidLayoutSubviews
方法中重新加载tableView将使其正常工作。
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.tableView reloadData];
}
原因您的代码无效的原因:
实际上,根据视图生命周期,方法按以下方式调用,init
,loadView
,viewDidLoad
,viewWillAppear
,viewWillLayoutSubviews
(不止一次调用) ),viewDidLayoutSubviews
(称为多次),最后viewDidAppear
。
现在,实际上tableview被加载到方法viewDidLoad
中,而你应用的自动布局约束将在该方法之后应用。这就是为什么,它没有显示出预期的结果。在应用自动布局约束后再次重新加载使其工作。那就是为什么上面的代码肯定会起作用。
还有一点要注意:在故事板中,如果你使用iphone 5大小设计了你的viewController,然后如果你在iphone 5中运行代码,然后不重新加载它,它必须工作,但如果你在任何其他运行它大小的viewController,然后它将无法正常工作。其背后的原因是,viewDidLoad
方法被调用,然后任何视图的大小都在其故事板中。
如果您有任何疑问,请告诉我
答案 2 :(得分:3)
Approach - 1 reload cell in viewWillAppear with delay mathod
func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.10, execute: {
self.tableView.reloadData() })
}
Approach - 2 Draw in GCD
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"TheCorrectCellIdentifier" forIndexPath:indexPath];
dispatch_async(dispatch_get_main_queue(), ^{
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:cell.bounds byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(4., 4.)];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = cell.bounds;
maskLayer.path = maskPath.CGPath;
cell.layer.masksToBounds = YES;
cell.layer.mask = maskLayer;
});
return cell;
}
答案 3 :(得分:2)
我无法在iOS 9.2上重现您的问题,因为您的代码对我来说运行正常。
如果您支持旧版本,我相信您遇到的问题与单元格的初始宽度有关,而不是与tableView的宽度相对应。在布置单元格之前,其初始宽度基于其Storyboard宽度(与实际屏幕宽度不同)。
您的shapeLayer蒙版会以错误的边界开始,因此在重复使用该单元格之前,该掩码将无法正确显示。此时,由于单元格已经布局,其宽度现在是正确的,新掩模的边界是正确的,并且实现了正确的效果。
虽然您可以尝试在cellForRowAtIndexPath
中执行布局,但它需要更多代码来处理边缘情况,例如初始单元格宽度问题。
在细胞布局之后应用遮罩要容易得多,例如technerd suggested in their answer。
这就是问题的解释。
这些变化涉及掩盖细胞以实现圆角底角。如果我设置了角半径(即cell.layer.cornerRadius = ...),这会使所有角落都圆,我没有看到问题。
由于这不涉及添加具有特定帧大小的形状图层,因此不会遇到问题。
表示渲染发生时,单元格的图层大小合适,因此圆角将正确显示。需要如何处理
我不知道你需要支持哪个版本的iOS(因为这个问题已在最近的版本中得到修复),但是这是解决这个问题的方法之一,其中单元格最初没有正确布局你在屏幕外滚动,然后回到屏幕上。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
TableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
// Workaround for visible cells not laid out properly since their layout was
// based on a different (initial) width from the tableView.
CGRect rect = cell.frame;
rect.size.width = CGRectGetWidth(tableView.bounds);
cell.frame = rect;
... configure cell
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
return cell;
}
你可以逃脱setNeedsLayout
; layoutIfNeeded
。在我的应用程序中,由于其他原因,我需要更新约束。
答案 4 :(得分:0)
我在调用tableView:cellForIndexPath期间修改了一个表视图单元格,但是在单元格滚动然后重新打开之前,修改不会出现。
这种方法不会被调用,直到细胞需要回收(即从屏幕外显示)。已经在tableview中的单元格不会调用此方法,因为它们已经可见。我想知道(因为这段代码工作正常)如果你混淆了委托回调?这些方法仅在新单元格滚动到视图(和缓存单元格)时触发 需要重新配置)。
如果您拨打- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
,则可以触发对单元格的重绘,或[tableview reloadData]
来刷新所有单元格
答案 5 :(得分:0)
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cell.reloadInputViews()
}