UICollectionView - 在一个视图中太多的更新动画 [英] UICollectionView - too many update animations on one view

查看:274
本文介绍了UICollectionView - 在一个视图中太多的更新动画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新:解决了!见下面的解决方案,我的答案。



我的应用程序显示在一个UICollectionView多个图像。而我目前遇到insertItemsAtIndexPaths一个问题,当越来越插入过快的集合视图来处理新项目。以下是例外:




NSInternalInconsistencyException原因:在一个视图太多更新动画
- 限制是在飞行31在一个时间




原来,这是由我的模型缓冲造成多达20个新的图像,并将它们在一次,但不是推到数据源集合中的查看批量更新块。由于没有批量更新的不就我而言懒惰,而是因为我的数据源之间的抽象层,实际上是一个.NET观察的集合(下面的代码)造成的。



我想知道的是被开发商应该如何预防打在飞行31动画硬编码的限制?我的意思是,当它发生时,你是敬酒。那么,什么是苹果想



注意,以MonoTouch的开发者阅读代码:



崩溃有效地被UICollectionViewDataSourceFlatReadOnly压倒性UIDataBoundCollectionView与它代理到代表的基本观察的集合的控制CollectionChanged事件引起的。在的CollectionView导致酩酊大醉与非批处理的 InsertItems 的来电。 (是保罗,它的一个ReactiveCollection)。



UIDataBoundCollectionView



  ///<总结>支持响应
///到数据源的变化自动更新,如果数据源支持INotifiyCollectionChanged
///<
/// UITableView的子类; /总结>
[注册(UIDataBoundCollectionView)]
公共类UIDataBoundCollectionView:UICollectionView,
IEnableLogger
{
公共重写NSObject的WeakDataSource
{
获得
{
返回base.WeakDataSource;
}


{
VAR NCC = base.WeakDataSource为INotifyCollectionChanged;
如果(NCC!= NULL)
{
ncc.CollectionChanged - = OnDataSourceCollectionChanged;
}

base.WeakDataSource =价值;

NCC = base.WeakDataSource为INotifyCollectionChanged;
如果(NCC!= NULL)
{
ncc.CollectionChanged + = OnDataSourceCollectionChanged;
}
}
}

无效OnDataSourceCollectionChanged(对象发件人,NotifyCollectionChangedEventArgs E)
{
NSIndexPath [] indexPaths;

开关(e.Action)
{
情况下NotifyCollectionChangedAction.Add:
indexPaths = IndexPathHelper.FromRange(e.NewStartingIndex,e.NewItems.Count);
InsertItems(indexPaths);
中断;

情况下NotifyCollectionChangedAction.Remove:
indexPaths = IndexPathHelper.FromRange(e.OldStartingIndex,e.OldItems.Count);
DeleteItems(indexPaths);
中断;

情况下NotifyCollectionChangedAction.Replace:
情况下NotifyCollectionChangedAction.Move:
PerformBatchUpdates(()=>
{
的for(int i = 0; I< ; e.OldItems.Count;我++)
移动选项(NSIndexPath.FromItemSection(e.OldStartingIndex + I,0),NSIndexPath.FromItemSection(e.NewStartingIndex + I,0));
},NULL) ;
中断;

情况下NotifyCollectionChangedAction.Reset:
ReloadData();
中断;
}
}
}



UICollectionViewDataSourceFlatReadOnly

  ///<总结> 
///绑定表到平(非分组)项目收集
///支持动态通过INotifyCollectionChanged
///<改变收藏; /总结>
公共类UICollectionViewDataSourceFlatReadOnly:UICollectionViewDataSource,
ICollectionViewDataSource,
INotifyCollectionChanged
{
///<总结>
///初始化℃的新的实例;参见CREF =UICollectionViewDataSourceFlat/>类。
///< /总结>
///< PARAM NAME =表>该表< /参数>
///< PARAM NAME =项目>该项目< /参数>
///&下; PARAM NAME =cellProvider>将细胞提供商下; /参数>
公共UICollectionViewDataSourceFlatReadOnly(IReadOnlyList<对象>的项目,ICollectionViewCellProvider cellProvider)
{
this.items =物品;
this.cellProvider = cellProvider;

//线了代理收集的变化,如果按来源
VAR NCC支持=项目,如INotifyCollectionChanged;
如果(NCC!= NULL)
{
//电线事件处理程序
ncc.CollectionChanged + = OnItemsChanged;
}
}

#区域属性
私人IReadOnlyList<对象>项目;
私人只读ICollectionViewCellProvider cellProvider; UICollectionViewDataSource

公共覆盖INT NumberOfSections(UICollectionView的CollectionView)
{
返回1
#endregion

#区域覆盖;
}

公共覆盖INT GetItemsCount(UICollectionView的CollectionView,INT部分)
{
返回items.Count;
}

///<总结>
///获取单元格。
///< /总结>
///< PARAM NAME =的tableView>该表视图< /参数>
///< PARAM NAME =indexPath>该指数路径< /参数>
///<&回报GT;< /回报>
公众覆盖UICollectionViewCell GetCell(UICollectionView的CollectionView,NSIndexPath indexPath)
{
//再利用或创造新的细胞
细胞无功=(UICollectionViewCell)collectionView.DequeueReusableCell(cellProvider.Identifier,indexPath );

//获取相关的集合项目
VAR项目=话getItemAt(indexPath);

//更新单元格
如果
cellProvider.UpdateCell(小区,项目,collectionView.GetIndexPathsForSelectedItems()包含(indexPath)。)(项目!= NULL);

//做
返回细胞;
}

#endregion

#地区ICollectionViewDataSource

///<的执行情况;总结>
///在获取项目。
///< /总结>
///< PARAM NAME =indexPath>该指数路径< /参数>
///<&回报GT;< /回报>
公共对象话getItemAt(NSIndexPath indexPath)
{
回报项目[indexPath.Item]
}

公众诠释ItemCount中
{
得到
{
返回items.Count;
}
}

#endregion

#地区INotifyCollectionChanged执行

// UIDataBoundCollectionView将认购本次活动
公共事件NotifyCollectionChangedEventHandler CollectionChanged;

#endregion

无效OnItemsChanged(对象发件人,NotifyCollectionChangedEventArgs E)
{
如果(CollectionChanged!= NULL)
CollectionChanged(发件人,E);
}
}


解决方案

酷! RxUI的最新版本拥有的UITableView的相似课程, ReactiveTableViewSource 。我也有一些棘手的问题, NSInternalInconsistencyException




  1. 如果您的任何更新都复位时,您需要了解如果应用程序增加了做一切

  2. 要忘记并删除了的相同的在相同的运行项目,你需要检测和抖它(即甚至不告诉UIKit的吧)。当你意识到添加/删除可以改变的范围的指标,而不仅仅是一个单一的指标这变得更加棘手。


Update: Solved! See my answer below for the solution.

My app displays a number of images in a UICollectionView. And I'm currently experiencing a problem with insertItemsAtIndexPaths when new items are getting inserted too fast for the collection view to handle. Below is the exception:

NSInternalInconsistencyException Reason: too many update animations on one view - limit is 31 in flight at a time

Turns out this was caused by my model buffering up to 20 new images and pushing them to the datasource at once but not inside a collection view batch update block. The absence of the batch update is not caused by laziness on my part but because of an abstraction layer between my datasource which is actually a .Net Observable collection (code below).

What I would like to know is how is the developer supposed to prevent hitting the hard coded limit of 31 animations in flight? I mean when it happens, you are toast. So what was Apple thinking?

Note to Monotouch Developers reading the code:

The crash is effectively caused by the UICollectionViewDataSourceFlatReadOnly overwhelming UIDataBoundCollectionView with CollectionChanged events which it proxies to the control on behalf of the underlying observable collection. Which results in the collectionview getting hammered with non-batched InsertItems calls. (yes Paul, its a ReactiveCollection).

UIDataBoundCollectionView

/// <summary>
/// UITableView subclass that supports automatic updating in response 
/// to DataSource changes if the DataSource supports INotifiyCollectionChanged
/// </summary>
[Register("UIDataBoundCollectionView")]
public class UIDataBoundCollectionView : UICollectionView,
  IEnableLogger
{
  public override NSObject WeakDataSource
  {
    get
    {
      return base.WeakDataSource;
    }

    set
    {
      var ncc = base.WeakDataSource as INotifyCollectionChanged;
      if(ncc != null)
      {
        ncc.CollectionChanged -= OnDataSourceCollectionChanged;
      }

      base.WeakDataSource = value;

      ncc = base.WeakDataSource as INotifyCollectionChanged;
      if(ncc != null)
      {
        ncc.CollectionChanged += OnDataSourceCollectionChanged;
      }
    }
  }

  void OnDataSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  {
    NSIndexPath[] indexPaths;

    switch(e.Action)
    {
      case NotifyCollectionChangedAction.Add:
        indexPaths = IndexPathHelper.FromRange(e.NewStartingIndex, e.NewItems.Count);
        InsertItems(indexPaths);
        break;

      case NotifyCollectionChangedAction.Remove:
        indexPaths = IndexPathHelper.FromRange(e.OldStartingIndex, e.OldItems.Count);
        DeleteItems(indexPaths);
        break;

      case NotifyCollectionChangedAction.Replace:
      case NotifyCollectionChangedAction.Move:
        PerformBatchUpdates(() =>
        {
          for(int i=0; i<e.OldItems.Count; i++)
            MoveItem(NSIndexPath.FromItemSection(e.OldStartingIndex + i, 0), NSIndexPath.FromItemSection(e.NewStartingIndex + i, 0));
        }, null);
        break;

      case NotifyCollectionChangedAction.Reset:
        ReloadData();
        break;
    }
  }
}

UICollectionViewDataSourceFlatReadOnly

/// <summary>
/// Binds a table to an flat (non-grouped) items collection 
/// Supports dynamically changing collections through INotifyCollectionChanged 
/// </summary>
public class UICollectionViewDataSourceFlatReadOnly : UICollectionViewDataSource,
  ICollectionViewDataSource,
  INotifyCollectionChanged
{
  /// <summary>
  /// Initializes a new instance of the <see cref="UICollectionViewDataSourceFlat"/> class.
  /// </summary>
  /// <param name="table">The table.</param>
  /// <param name="items">The items.</param>
  /// <param name="cellProvider">The cell provider</param>
  public UICollectionViewDataSourceFlatReadOnly(IReadOnlyList<object> items, ICollectionViewCellProvider cellProvider)
  {
    this.items = items;
    this.cellProvider = cellProvider;

    // wire up proxying collection changes if supported by source
    var ncc = items as INotifyCollectionChanged;
    if(ncc != null)
    {
      // wire event handler
      ncc.CollectionChanged += OnItemsChanged;
    }
  }

  #region Properties
  private IReadOnlyList<object> items;
  private readonly ICollectionViewCellProvider cellProvider;
  #endregion

  #region Overrides of UICollectionViewDataSource

  public override int NumberOfSections(UICollectionView collectionView)
  {
    return 1;
  }

  public override int GetItemsCount(UICollectionView collectionView, int section)
  {
    return items.Count;
  }

  /// <summary>
  /// Gets the cell.
  /// </summary>
  /// <param name="tableView">The table view.</param>
  /// <param name="indexPath">The index path.</param>
  /// <returns></returns>
  public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
  {
    // reuse or create new cell
    var cell = (UICollectionViewCell) collectionView.DequeueReusableCell(cellProvider.Identifier, indexPath);

    // get the associated collection item
    var item = GetItemAt(indexPath);

    // update the cell
    if(item != null)
      cellProvider.UpdateCell(cell, item, collectionView.GetIndexPathsForSelectedItems().Contains(indexPath));

    // done
    return cell;
  }

  #endregion

  #region Implementation of ICollectionViewDataSource

  /// <summary>
  /// Gets the item at.
  /// </summary>
  /// <param name="indexPath">The index path.</param>
  /// <returns></returns>
  public object GetItemAt(NSIndexPath indexPath)
  {
    return items[indexPath.Item];
  }

  public int ItemCount
  {
    get
    {
      return items.Count;
    }
  }

  #endregion

  #region INotifyCollectionChanged implementation

  // UIDataBoundCollectionView will subscribe to this event
  public event NotifyCollectionChangedEventHandler CollectionChanged;

  #endregion

  void OnItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
  {
    if(CollectionChanged != null)
      CollectionChanged(sender, e);
  }
}

解决方案

Cool! The latest version of RxUI has a similar class for UITableView, ReactiveTableViewSource. I also had some tricky issues with NSInternalInconsistencyException:

  1. If any of your updates are a Reset, you need to forget about doing everything else
  2. If the app has added and removed the same item in the same run, you need to detect that and debounce it (i.e. don't even tell UIKit about it). This gets even trickier when you realize that Add / Remove can change a range of indices, not just a single index.

这篇关于UICollectionView - 在一个视图中太多的更新动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆