ios - Animated scroll-to-item in UICollectionView doesn't always work -


problem

i'd make uicollectionview animated scroll specific item.

this works of time, item i'm trying scroll doesn't end being shown.

code

- (void)onclick {   // (possibly recompute _items array.)   nsinteger target_idx = // (...some valid index of _items)   nsindexpath *item_idx = [nsindexpath indexpathforitem:target_idx insection:0];   [self scrolltoitem:item_idx]; }  - (void)scrolltoitem:(nsindexpath*)item_idx {   // make sure our view up-to-date data want show.   nsinteger num_items = [self.collection_view numberofitemsinsection:0];   if (num_items != _items.count) {     [self.collection_view reloaddata];   }    [self.collection_view      scrolltoitematindexpath:item_idx            atscrollposition:uicollectionviewscrollpositioncenteredhorizontally                    animated:yes]; } 

details

  • self.collection_view uicollectionview consists of single row of items, standard flow layout , horizontal scrolling enabled.
  • i need call reloaddata before scrolling, because it's possible _items has changed since uicollectionview last displayed it.
  • the problem happens animated scrolling; if pass animated:no works expected.
  • when problem happens, post-scroll call indexpathsforvisibleitems reveals uicollectionview doesn't think target item visible.

any ideas why scrolling item silently fails sometimes?


update: problem seems come reloading , scrolling in quick succession; if there no reload, scrolling behaves expected. there idioms scrolling item after loading new data?

i've run in similar / same issue after adding item uicollectionview.

what's happening

the issue seems following [collectionview reloaddata] or [collectionview insertitemsatindexpaths: @[newitemindexpath]], the collection view's content size not yet updated.

if try scroll added item visible, fail because the content rect doesn't yet include space new item.

a fix

there simple , robust work around, post scroll event on next iteration of run loop this:

const nsuinteger newindex =    [self collectionview: self.collectionview numberofitemsinsection: 0] - 1;  nsindexpath *const newpath =    [nsindexpath indexpathforitem: newindex                       insection: 0];  [self.collectionview insertitemsatindexpaths: @[newpath]];  uicollectionviewlayoutattributes *const layoutattributes =   [self.collectionview layoutattributesforitematindexpath: newpath];  dispatch_async(dispatch_get_main_queue(), ^{     [self.collectionview scrollrecttovisible: layoutattributes.frame                                      animated: yes]; }); 

isn't there nicer fix?

while works, "post scroll call on next run loop tick" shenanigans feels hacky.

it preferable if uicollectionview invoke callback when finished updating content rect. uikit has callbacks of style other methods perform asynchronous updates model. example, completion block uiviewcontroller transitions , uiview animations.

uicollectionview not provide callback. far know, there no other simple clean way find when completes update. next run loop tick proxy callback want.

anything else know?

it's useful know uitableview has issue. similar workaround should work there too.


Comments