Monday 9 December 2013

"Infinite Scrolling" for Unity3D Using NGUI's UIScrollView (1st attempt)

For latest update please check this post instead
I've been using NGUI for UI related work on my Unity project for the past few weeks and it is a breath of fresh air when compared to the stock UnityGUI (OnGUI()).

Part of my project relate to display a relatively large amount of data dynamically (well not very large but in thousands) and instantiating a prefab for each data element didn't seem like a good idea to me :-)

So instead I decided to add some logic to do a scroll view with fixed pool of items that I reuse and position according to the direction of the scroll and get it fed with the correct data.

Although I am sure that there are existing solutions I decided to do my own.

The logic so far is built using UIGrid with fixed cell height for the moment (not well suited for UITable with different cell height) and the panel is using soft clip. the Scroll view is using momentum only (Spring physics breaks my current logic for some reason)

Initialization steps:
  1. Pool size is calculated using the cell height against the panel height plus some buffer (currently I am using a buffer of 4 list items)
  2. List items' pool is instantiated with corresponding data from the data list
  3. A map is maintained to keep track of which data item are used in which list items
While Scrolling:
  1. Any item that turns a visible from an invisible state will notify the main controller
  2. We check the direction of the scroll based on which item is visible (e.g. if the next item is visible means that we are dragging upwards)
  3. based on the direction we reuse the items at the top or the bottom of the list accordingly (e.g. if direction is up the top item moves to the bottom and get populated with data from the corresponding data element)

This is a first attempt and further posts will follow as the logic evolve.

I am in the process of making this component available as open source.
Meanwhile we've launched a free app on Android that uses it called Avatar Messenger
==UPDATE 2==
 The component is available as open source


  1. its just what I´m trying to do. Nice post Ill check out further post

  2. Thanks, it's exactly what I need. But can you explain how do you change the order of items while scrolling? Do you actualy change it's position or it is done using sorting by name?

    1. Hi,

      I am planning to make another detailed post once I have the time as I do now have a more robust solution.

      Did you mean item index in the data list? then no I don't change the order

      In short i have two lists: one is the data list in whatever order you would want which I reference ( item index is not changed in this list) and the other is items pool list which keep track of data item index its representing and I position it in the table according to that index. hope this is clear

    2. This comment has been removed by the author.

    3. Thanks for your reply! Some more questions:

      1."...Any item that turns a visible from an invisible state will notify the main controller"
      How do you detect changing of items visible state? I tried OnEnamle() method, but it doesn't work

      2 "...based on the direction we reuse the items at the top or the bottom of the list accordingly (e.g. if direction is up the top item moves to the bottom and get populated with data from the corresponding data element)"

      How do you detect current top/bottom item and move it to bottom/top of the UIGrid? I was looking thrue NGUI documentation, but i still can't find an answer.

      I would appreciate your help, because I'm really stuck at this two points

    4. 1. for that I am checking using UIPanel IsVisible() method

      2. There are two ways to check for scrolling direction: you can either check against scrollview currentMomentum property (y value in this case)... or you can check against the current item that became invisible (I am doing this at the moment): if the item above the invisible item is visible then scrolling down and vice versa

    5. and the current top and bottom items will corresponds to your pool of objects.

  3. Thanks for that! Direction is not where i'm stuck. The point is, how exactly to get the top and bottom items, which I'll need to move? What method do you use to get those gameobjects?

    Do you move the top item to the bottom using transform.position and after that calling Reposition() method of UIGrid? I guess, there is some other way to do it, but I still don't get it.

    Please, excuse me for being annoying:)

    1. No I don't use Reposition method in this case. I just change the transform position of the top and bottom items (either subtract cellheight * poolSize or add cellheight* poolsize depending on which item you need to move (top or bottom))

      I keep track of all gameobjects (the pool items) using a hashmap of indexes (data index as a key and a gameobject index as a value) which i keep updating when repositioning the items to represent new data...

    2. Thank you very much! I'll try to do that.
      Have you try to use NData to provide automatic data binding in the infinite scrolling table? If so, could you give any example of that?

  4. Tell me please, how do you avoid inaccuracy in positioning elements while scrolling? I mean, for example, when I move the top item to the bottom while fast scrolling the inaccuracy in y coordinate appears and every time it becomes bigger.

    Can you post your source code? It would be really helpful!

    1. sorry for my late reply as I didnt notice your comment. as I mentioned below I can't share the full source(for now) but I will update with some snippets when I get the chance

  5. Replies
    1. I am looking into open sourcing the component and releasing it in the asset store for free so people can update it as they wish.. need to clear some legal stuff with my employer first since I developed the component while making them an app instead of using my free time. But before that I will update the blog with more info on how I did it and some snippets

    2. I have hard trouble with scrolling. I use uiscrollview + uigrid, so i dynamic fill grid. If count items < 50 all work quick, after 100 items i see very hard freeze and memory leaks, i use OnBecameInvisible and OnBecameVisible for all items. If i scroll CPU say me WTF 0_0 ))) So can you send you source\sample to I would be most grateful.

    3. I will let you know once the code in GitHub which should be soon enough ( I will make a post with the code as well) in the meantime make sure to disable the colliders for invisible items

  6. Hope post sources or some snippets. Meet same problem in my card game, create more than 300 prefabs in one frame cause 2 secs freeze... Also want to known how to set scrollbar correct in your way.

    1. Hi Brian,
      I've just managed to make the component code as open source in github:
      At the moment I don't support full NGUI scroll bar (I made a scroll indicator i.e. not interactive)

  7. hey I am getting this error when i am runninig infinite list demo scene

    transform.localPosition assign attempt for 'ScrollIndicator' is not valid. Input localPosition is { 0.568161, NaN, 0.000000 }.
    InfiniteListPopulator:Update() (at Assets/InfiniteListDemo/InfiniteListPopulator.cs:146)