滚动关闭然后返回

时间:2016-01-25 02:24:09

标签: ios uitableview calayer

我在调用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;
}

6 个答案:

答案 0 :(得分:26)

您的代码正常运行。但如果您仍然在cellForRowAtIndexPath:中遇到角半径问题,请尝试willDisplayCell:。我尝试了两种方法并且工作正常。

willDisplayCell:

  

告诉委托表视图是否要绘制一个单元格   特定行。

  表视图仅将此消息发送给其委托   在它使用单元格绘制一行之前,从而允许代表进行   在显示单元格对象之前自定义它。这种方法给出了   委托有机会覆盖先前设置的基于状态的属性   通过表格视图,例如选择和背景颜色。之后   委托返回,表视图仅设置alpha和frame   属性,然后仅在向行或向外滑动动画时动画。   这是我的代码。

据我所知,Apple提供了两套不同的方法(协议),名为UITableViewDataSourceUITableViewDelegate来管理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;
}

输出:

enter image description here

答案 1 :(得分:6)

viewDidAppearviewDidLayoutSubviews方法中重新加载tableView将使其正常工作。

-(void)viewDidAppear:(BOOL)animated
{
 [super viewDidAppear:animated];
 [self.tableView reloadData];
}

原因您的代码无效的原因: 实际上,根据视图生命周期,方法按以下方式调用,initloadViewviewDidLoadviewWillAppearviewWillLayoutSubviews(不止一次调用) ),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()
}