【问题标题】:UITableView row delete animation flickers on iOS 7iOS 7上的UITableView行删除动画闪烁
【发布时间】:2014-04-17 09:28:50
【问题描述】:

我的应用有一个 UITableView,其中包含具有透明背景的可变高度单元格。针对 iOS 7 SDK 构建时,行删除动画存在问题:

注意某些单元格的闪烁,例如第 15、17、21 和 23 行。在 iOS

如果我做任何这些事情,问题就会消失(我还没有准备好做这些事情):

  • 始终创建新单元,而不是使可重复使用的单元出列
  • 为细胞提供非透明背景
  • 赋予所有行相同的高度

我尝试过的其他方法都没有帮助。 iOS 7 上是否需要一些秘诀来获得平滑的行删除动画?

这是我可以将问题提炼为 [C#/Xamarin] 的最小测试用例:

public class MainViewController : UITableViewController {

    public MainViewController() {
        this.TableView.Source = new TableSource(this.TableView, 50);
    }

    public override void ViewDidAppear(bool animated) {
        base.ViewDidAppear(animated);
        NSTimer.CreateRepeatingScheduledTimer(0.5f, ((TableSource) this.TableView.Source).DeleteFirstRow);
    }
}

public class TableSource : UITableViewSource {

    private UITableView tableView;
    private List<RowInfo> activeRows;

    public TableSource(UITableView tableView, int numRows) {

        this.tableView = tableView;

        this.activeRows = new List<RowInfo>();
        for (int i=1; i<=numRows; i++) {
            this.activeRows.Add(new RowInfo(i));
        }
    }

    public void DeleteFirstRow() {

        if (this.activeRows.Count == 0) return;

        this.activeRows.RemoveAt(0);

        this.tableView.BeginUpdates();
        this.tableView.DeleteRows(new NSIndexPath[] { NSIndexPath.FromRowSection(0, 0) }, UITableViewRowAnimation.Right);
        this.tableView.EndUpdates();
    }

    public override int RowsInSection(UITableView tableview, int section) {
        return this.activeRows.Count;
    }

    public override float GetHeightForRow(UITableView tableView, NSIndexPath indexPath) {
        return this.activeRows[indexPath.Row].Height;
    }

    public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath) {

        const string CellID = "Cell";

        UITableViewCell cell = tableView.DequeueReusableCell(CellID);
        if (cell == null) {
            cell = new UITableViewCell(UITableViewCellStyle.Default, CellID);
        }
        cell.TextLabel.Text = this.activeRows[indexPath.Row].Text;

        return cell;
    }

    public override void WillDisplay(UITableView tableView, UITableViewCell cell, NSIndexPath indexPath) {
        cell.BackgroundColor = UIColor.Clear;
    }

    private class RowInfo {

        public RowInfo(int index) {
            Random random = new Random(index);
            this.Height = random.Next(20, 80);
            this.Text = "Row " + index + " has height " + this.Height;
        }

        public float Height;
        public string Text;
    }
}

【问题讨论】:

  • 它是在设备上以及模拟器上还是在模拟器上完成?
  • 是的。在 iPhone 和 iPad sims、iPhone 5s 和 iPad 3 上看到的行为,都在 iOS 7.1 上。
  • 你能解决这个问题吗?我也面临同样的问题。
  • 我也遇到过很多这个问题,我发现的唯一解决方法(几乎不是解决方案)是切换到 UITableViewRowAnimationFade - 这似乎比右/左滑动面板运行得更顺畅' 类型动画。对不起:(
  • 不幸的是,这在 iOS 8.3 中仍未修复。但是 user3099609 提出的解决方法是有效的。

标签: ios ios7 uitableview


【解决方案1】:

TLDR:cell.clipsToBounds = YES;

====

这是我发现的:

'   |    |    |    | <UITableViewCell: 0x79e81910; frame = (0 232; 320 32); text = 'row 27'; clipsToBounds = YES; autoresize = W; animations = { position=<CABasicAnimation: 0x79e8f020>; }; layer = <CALayer: 0x79e81aa0>>
   |    |    |    |    | <UITableViewCellScrollView: 0x79e81ad0; frame = (0 0; 320 32); autoresize = W+H; gestureRecognizers = <NSArray: 0x79e81d60>; layer = <CALayer: 0x79e81ca0>; contentOffset: {0, 0}>
   |    |    |    |    |    | <UIView: 0x79e8b7d0; frame = (0 0; 320 51); clipsToBounds = YES; layer = <CALayer: 0x79e8b830>>
   |    |    |    |    |    | <UITableViewCellContentView: 0x79e81f50; frame = (0 0; 320 31.5); gestureRecognizers = <NSArray: 0x79e82160>; layer = <CALayer: 0x79e81fc0>>
   |    |    |    |    |    |    | <UILabel: 0x79e821b0; frame = (15 0; 290 31.5); text = 'row 27'; userInteractionEnabled = NO; layer = <CALayer: 0x79e82260>>
   |    |    |    |    |    | <_UITableViewCellSeparatorView: 0x79e824d0; frame = (15 31.5; 305 0.5); layer = <CALayer: 0x79e82540>>

如果您仔细查看第 27 行,您会注意到单元格的高度为 32 像素。单元格的子视图是一个滚动视图(我在某处读过,这是您向左滑动以显示 iOS7 中的行操作的滚动视图)。正如我们所见,contentView 的正确高度是 32px,但是,有趣的部分从这里开始:在 contentView 后面有一个视图,高度为 51px。我相信这是罪魁祸首。

不幸的是,我无法找到那个视图(它不是 backgroundView,它不是 selectedBackgroundView 等,简而言之:它不是 UITableViewCell 类的公共属性中的任何视图)。

另外,奇怪的是,该视图的 backgroundColor 是 clearColor。理论上它甚至不应该是可见的!我相信我们在这里处理的是一些 CoreAnimation 优化,它不会渲染这个神秘视图背后的区域。

所以,会发生什么:1) 单元格被创建 2) 单元格被调整大小并准备呈现 3) 单元格添加了正确大小的神秘视图 4) 单元格被重用 5) 单元格被调整为新的动态高度 6) scrollView 和 contentView 也可以调整大小,但神秘视图没有(请注意它没有设置任何调整大小掩码)。

这解释了为什么您可以通过上述三种方式中的任何一种来解决此问题。

解决方法是在创建时设置单元格的cell.clipsToBounds = YES;。 我还建议不要设置 cell.backgroundColor。 IIRC,您应该为此使用 cell.backgroundView (将其设置为新创建的视图并将其设置为 backgroundColor )。然而,单元格的默认 bacgroundColor 无论如何都是清晰的,因此在这种情况下这并不重要。

我很高兴听到有人提供此私人视图的用途。

【讨论】:

  • 这就是我想要的。谢谢