Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Optimized ScrollView Adapter + Playmaker support

Discussion in 'Assets and Asset Store' started by xucian, Apr 1, 2016.

  1. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Hey, thanks!

    The overall UX in all of the common cases (presented in the demos) should be butter-smooth. If any issues like unexpected content jumps or item resizes are seen, it means there's some improper usage.

    So you basically have a variable text size and a variable image size, which isn't significantly different from the OSA's "ContentSizeFitter" example. Do you follow that demo's scene/code strictly? Also, there's a manual attached just to that use-case, since it's very common: link.
    I assume the OSA's CSF demo doesn't show this issue, right? Because if it does, then it may imply a Unity bug that we'll need to workaround. Let me know

    Also, I don't immediately see why you'd need to store the item's height in the model. After all, the LayoutGroup + CSF should take care of that (if implemented according to the manual).
    Also, make sure to read any note that's in the demo scene (it's outside the screen).

    -- Lucian
     
  2. jmansa

    jmansa

    Joined:
    Apr 13, 2012
    Posts:
    75
    Hi Lucian,

    I have tried to follow the example, but am still having the issue. Let me show you what I do, and maybe you or another can see the issue :-/

    My adapterscript:
    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using frame8.Logic.Misc.Other.Extensions;
    6. using Com.TheFallenGames.OSA.Core;
    7. using Com.TheFallenGames.OSA.CustomParams;
    8. using Com.TheFallenGames.OSA.DataHelpers;
    9.  
    10.  
    11. public class BasicWallAdapter : OSA<MyParams, MyListItemViewsHolder>
    12. {
    13.     // LETS GET DATA FROM DATABASE //
    14.     public GSStart gsstart;        // Database script
    15.     public int loadFrom = 0;    // The initial number to start getting records from
    16.  
    17.     public bool _Fetching;
    18.     public bool _LoadedAll;
    19.  
    20.     protected override void OnEnable()
    21.     {
    22.         gsstart.onConnectDataResponse += onConnectDataResponse;            // Check if connection is OK
    23.         gsstart.onGetWallStoriesResponse += onGetWallStoriesResponse;    // Response when data has been fetched
    24.     }
    25.    
    26.     void onConnectDataResponse(bool isConnected)
    27.     {
    28.         if (isConnected)
    29.         {
    30.             gsstart.GetUser(); // First, get user
    31.             _Fetching = true;
    32.         }
    33.     }
    34.  
    35.     // Helper that stores data and notifies the adapter when items count changes
    36.     // Can be iterated and can also have its elements accessed by the [] operator
    37.     public SimpleDataHelper<WallStory> Data { get; private set; } // Not sure if I should use lazyDataHelper?!?
    38.  
    39.  
    40.     #region OSA implementation
    41.     protected override void Start()
    42.     {
    43.         Data = new SimpleDataHelper<WallStory>(this);
    44.  
    45.         // Calling this initializes internal data and prepares the adapter to handle item count changes
    46.         base.Start();
    47.  
    48.     }
    49.  
    50.     protected override void Update()
    51.     {
    52.         base.Update();
    53.  
    54.         if(!IsInitialized)
    55.             return;
    56.  
    57.         if (_Fetching)
    58.             return;
    59.  
    60.         if (_LoadedAll)
    61.             return;
    62.  
    63.         int lastVisisbleItemIndex = -1;
    64.         if(VisibleItemsCount > 0)
    65.             lastVisisbleItemIndex = GetItemViewsHolder(VisibleItemsCount - 1).ItemIndex;
    66.  
    67.         int numberOfItemsBelowLastVisible = Data.Count - (lastVisisbleItemIndex + 1);
    68.  
    69.         // If the number of items available below the last visible (i.e. the bottom-most one, in our case) is less than <adapterParams.preFetchedItemsCount>, get more
    70.         if (numberOfItemsBelowLastVisible < _Params.preFetchedItemsCount)
    71.         {
    72.             int newPotentialNumberOfItems = Data.Count + _Params.preFetchedItemsCount;
    73.             if (_Params.totalCapacity > -1) // i.e. the capacity isn't unlimited
    74.                 newPotentialNumberOfItems = Mathf.Min(newPotentialNumberOfItems, _Params.totalCapacity);
    75.  
    76.             if (newPotentialNumberOfItems > Data.Count) // i.e. if we there's enough room for at least 1 more item
    77.             {
    78.                 StartPreFetching(newPotentialNumberOfItems - Data.Count);
    79.             }
    80.         }
    81.     }
    82.  
    83.     void StartPreFetching(int additionalItems)
    84.     {
    85.         _Fetching = true;
    86.  
    87.         gsstart.GetWallstories(loadFrom); // Requesting the database for more records
    88.     }
    89.  
    90.     // This is called initially, as many times as needed to fill the viewport,
    91.     // and anytime the viewport's size grows, thus allowing more items to be displayed
    92.     // Here you create the "ViewsHolder" instance whose views will be re-used
    93.     // *For the method's full description check the base implementation
    94.     protected override MyListItemViewsHolder CreateViewsHolder(int itemIndex)
    95.     {
    96.         var instance = new MyListItemViewsHolder();
    97.  
    98.         instance.Init(_Params.ItemPrefab, _Params.Content, itemIndex);
    99.  
    100.         return instance;
    101.     }
    102.  
    103.     protected override void OnItemHeightChangedPreTwinPass(MyListItemViewsHolder viewsHolder)
    104.     {
    105.         base.OnItemHeightChangedPreTwinPass(viewsHolder);
    106.     }
    107.  
    108.     // This is called anytime a previously invisible item become visible, or after it's created,
    109.     // or when anything that requires a refresh happens
    110.     // Here you bind the data from the model to the item's views
    111.     // *For the method's full description check the base implementation
    112.     protected override void UpdateViewsHolder(MyListItemViewsHolder newOrRecycled)
    113.     {
    114.         // In this callback, "newOrRecycled.ItemIndex" is guaranteed to always reflect the
    115.         // index of item that should be represented by this views holder. You'll use this index
    116.         // to retrieve the model from your data set
    117.        
    118.         WallStory model = Data[newOrRecycled.ItemIndex];
    119.  
    120.         ScheduleComputeVisibilityTwinPass(_Params.freezeContentEndEdgeOnCountChange);
    121.  
    122.         newOrRecycled.titleText.text = model.story;
    123.         newOrRecycled.pictureContainer.gameObject.SetActive(false);
    124.  
    125.         if(model.mediaHeight > 0 && model.pictures.Count > 0)
    126.         {
    127.             newOrRecycled.storyPicture.gameObject.SetActive(false);
    128.             newOrRecycled.loadingText.gameObject.SetActive(true);
    129.             newOrRecycled.pictureContainer.gameObject.SetActive(true);
    130.  
    131.             newOrRecycled.pictureContainer.sizeDelta = new Vector2(640f, model.mediaHeight);
    132.             newOrRecycled.storyPicture.gameObject.GetComponent<RectTransform>().sizeDelta = new Vector2( 640f, model.mediaHeight);
    133.  
    134.             PictureManager.instance.LoadPicture(model.pictures[0], newOrRecycled.storyPicture, newOrRecycled.loadingText);
    135.         }
    136.  
    137.     }
    138.  
    139.     #endregion
    140.  
    141.  
    142.     // Retrieving <count> models from the data source and calling OnDataRetrieved after.
    143.     // In a real case scenario, you'd query your server, your database or whatever is your data source and call OnDataRetrieved after
    144.     void onGetWallStoriesResponse(List<WallStory> tempStories, int loaded)    // Data recieved
    145.     {
    146.         loadFrom = loaded; // Setting the new loadfrom
    147.  
    148.         var newItems = new WallStory[tempStories.Count];
    149.  
    150.         for (int i = 0; i < tempStories.Count; ++i)
    151.         {
    152.             newItems[i] = tempStories[i];
    153.         }
    154.  
    155.         _Fetching = false;
    156.  
    157.         OnDataRetrieved(newItems);
    158.     }
    159.  
    160.  
    161.     void OnDataRetrieved(WallStory[] newItems)
    162.     {
    163.         Data.InsertItemsAtEnd(newItems);
    164.     }
    165. }
    166.  
    167. [Serializable]
    168. public class MyParams : BaseParamsWithPrefab
    169. {
    170.     public int preFetchedItemsCount;
    171.     [Tooltip("Set to -1 if while fetching <preFetchedItemsCount> items, the adapter shouldn't check for a capacity limit")]
    172.     public int totalCapacity;
    173.  
    174.     [NonSerialized]
    175.     public bool freezeContentEndEdgeOnCountChange;
    176. }
    177.  
    178.  
    179. // This class keeps references to an item's views.
    180. // Your views holder should extend BaseItemViewsHolder for ListViews and CellViewsHolder for GridViews
    181. public class MyListItemViewsHolder : BaseItemViewsHolder
    182. {
    183.    
    184.     public Text titleText;
    185.     public Text loadingText;
    186.     public RectTransform pictureContainer;
    187.     public RawImage storyPicture;
    188.  
    189.     ContentSizeFitter CSF { get; set; }
    190.    
    191.  
    192.     // Retrieving the views from the item's root GameObject
    193.     public override void CollectViews()
    194.     {
    195.         base.CollectViews();
    196.  
    197.         // GetComponentAtPath is a handy extension method from frame8.Logic.Misc.Other.Extensions
    198.         // which infers the variable's component from its type, so you won't need to specify it yourself
    199.  
    200.         CSF = root.GetComponent<ContentSizeFitter>();
    201.         CSF.enabled = false;
    202.  
    203.         root.GetComponentAtPath("TitlePanel/TitleText", out titleText);
    204.         root.GetComponentAtPath("PicturePanel", out pictureContainer);
    205.         root.GetComponentAtPath("PicturePanel/Picture", out storyPicture);
    206.         root.GetComponentAtPath("PicturePanel/LoadingText", out loadingText);
    207.  
    208.     }
    209.  
    210.     // Override this if you have children layout groups or a ContentSizeFitter on root that you'll use.
    211.     // They need to be marked for rebuild when this callback is fired
    212.     public override void MarkForRebuild()
    213.     {
    214.         base.MarkForRebuild();
    215.  
    216.         if (CSF)
    217.         {
    218.             CSF.enabled = true;
    219.         }
    220.     }
    221.    
    222.  
    223.     // Override this if you've also overridden MarkForRebuild() and you have enabled size fitters there (like a ContentSizeFitter)
    224.     public override void UnmarkForRebuild()
    225.     {
    226.         if (CSF)
    227.         {
    228.             CSF.enabled = false;
    229.         }
    230.         base.UnmarkForRebuild();
    231.     }
    232.  
    233. }
    My prefab looks as follows:


    And the components on the prefab like this:


    Hope you or someone can see what I am missing :-/

    Thanks in advance :)
     
  3. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    This may come later than you probably needed it, but it was a busy week here. Sorry.
    From what I see, there's not something obviously wrong in your code. Please send me a video with the actual issue on discord and we'll go from there.

    -- Lucian
     
  4. novaVision

    novaVision

    Joined:
    Nov 9, 2014
    Posts:
    515
    Hi @thefallengamesstudio

    Can you advice, is there any quick solution to "pin" UI elements to the top of the scroll content without creating separate model and reload it each time on this block (red rectangles on the screenshot) become visible/invisible?
    Optimized ScrollView Adapter + Playmaker support | Page 12 - Unity Forum 2020-09-12 10-26-38.png

    In my case if would be enough even without disabling/enabling these red blocks - just let them always be visible, even when they are outside the scroll rect view area.
     
  5. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Hey,

    If you want the red items to be pre-made and not be part of the OSA's list of items (i.e. not affecting the items count, viewsholders etc.), you can use OSAContentDecorator.
    Check the linked post and let me know if it helped. :)

    -- Lucian
     
  6. novaVision

    novaVision

    Joined:
    Nov 9, 2014
    Posts:
    515
    Perfect, thanks, that's exactly what I needed
     
    xucian likes this.
  7. novaVision

    novaVision

    Joined:
    Nov 9, 2014
    Posts:
    515
    Sorry, but one more question related to OSAContentDecorator usage.
    I can add this object only inside OSA Viewport, what make impossible to me control the scrollable items width in cases, when I need ContentDecorator to fit entire width of the screen.
    Here is a schema - Optimized ScrollView Adapter + Playmaker support | Page 12 - Unity Forum 2020-09-13 12-41-51.png

    Is there any way to miss OSA scrollable items width change? Now it always set it according to Paddings properties
     
  8. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    The decorator is not affected by padding. It has the same size that you currently assign to it.
    But there's a small issue that I've just discovered now.
    This will make it into the next patch version, but until then, here's what I've changed. Go to OSAContentDecorator.Init() and change
    Code (CSharp):
    1.             var aPos = _RT.localPosition;
    2.             _RT.anchorMin = _RT.anchorMax = new Vector2(0f, 1f); // top-right
    3.             _RT.localPosition = aPos;
    to
    Code (CSharp):
    1.             // Improvement 14.09.2020: this was limiting - each user should be able to set of their own anchors for maximum flexibility. OSA should only control the decorator's position
    2.             //var aPos = _RT.localPosition;
    3.             //_RT.anchorMin = _RT.anchorMax = new Vector2(0f, 1f); // top-right
    4.             //_RT.localPosition = aPos;
    Then set the decorator's X/Y anchors in editor accordingly.

    An additional note that I've added to the class' summary:
    Code (CSharp):
    1.     /// If you use Unity 2019.3.5f1 (probably there are other buggy versions as well), this won't work properly if you don't bring the anchors together in the scrolling direction.
    2.     /// It's a bug in Unity where a RectTransform's size isn't correctly reported in Awake(), and it affects all UI components, not only this one
     
  9. novaVision

    novaVision

    Joined:
    Nov 9, 2014
    Posts:
    515
    Thanks, will change it, but I had no problems with decorator at all. I been talking about avoiding scrollable items width change on item initialization according to the padding.
    Let's take an example:
    • canvas - screen width is 1000px
      • OSA vertical anchored to screen corners - width is 1000px as well
        • viewport - 1000px
        • decorator - 1000px (anchored to top OSA RectTransform corners) Decorator always must be stretched to screen width, but because of I can't place it outside the OSA RT, OSA RT must be stretched to the screen width
          • content - need to set it 700px, but it's size always updated on initialization according to Padding settings
    Hope it's clear :D
     
  10. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Oh, you need a constant transversal size for the content (width, in this case)? If yes, then check the description of BaseParams.ItemsTransversalSize. It should cover all of your needs. :)
    This will affect the items' width in your case. The game object "Content" won't be changed because of how OSA works, but if you want a background for your items which is also 700px, you'll need to add one manually (either set its size in editor or at runtime - your choice).
     
    novaVision likes this.
  11. novaVision

    novaVision

    Joined:
    Nov 9, 2014
    Posts:
    515
    I found another bug:
    if I set inset to any value in px (when normalized flag turned off) scroll content padding calculated incorrectly - it doesn't take in account inset value
     
  12. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Can you show me some code and an image with before and after executing it?
    I'd also like to know:
    - the size of the ScrollView and Viewport in px (look in inspector - bring their anchors together if you want to see Width/Height instead of left/right etc.)
    - the OSA's padding setting
    - the OSA's spacing setting
     
  13. novaVision

    novaVision

    Joined:
    Nov 9, 2014
    Posts:
    515
    Hope this helps
    Also I get this log on start:
    Code (csharp):
    1.  
    2. OSA's content padding can't be controlled if Inset (50) is not 0 .
    3.  
     
  14. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Actually, that can be less restrictive. I've updated the decorator script, and you can get it early here.

    I think it should solve most if not all of the issues you've presented. Otherwise, let me know.

    -- Lucian
     
  15. novaVision

    novaVision

    Joined:
    Nov 9, 2014
    Posts:
    515
    Can't join this channel, need to verify me, but you didn't respond in PM.
    I found several other issues related to that decorator component... unfortunately it doesn't work stable in all some cases.
     
  16. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Verified now :)
     
  17. jhjoo_unity

    jhjoo_unity

    Joined:
    Jan 17, 2020
    Posts:
    5
    hi.

    I am using osa 5.1.1.

    Need to use the drag function from A List to B List.

    I want the item in A List to be copied only to B List without disappearing.
    (Don't even need the sorting function in A List.)

    Also want to use the multi-select drag function.

    Please help me......
    Thanks!
     
    Last edited: Oct 21, 2020
  18. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Hello,

    Sorry for taking this long to respond. Usually, it's max 2 days between replies.

    Any additional functionality for the demo scenes that was requested until now was implemented, but it's not something that I can focus on anymore, because of my main job. I can only help with fixes/patches and critical features that are missing. What you need is out of OSA's scope, as a package, but I would've implemented it, if the time would've still allowed me to.

    As an implementation tip, you might be better off starting from the "select_and_delete" scene and implement your own dragging system, as that'll be easier. The "item_dragging" demo focuses on having an actual item moved from a deck to another. Moving a copy of an item is infinitely easier because you just clone its views - there's less housekeeping to do.
    But you can look into the "item_dragging" as well, to see how the dragging events are managed (OnBeginDrag, OnDrag etc.).
    Hope this was helpful.

    -- Lucian
     
  19. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    OSA is now 50% OFF for a few days!
     
  20. eijnew

    eijnew

    Joined:
    May 23, 2017
    Posts:
    1
    Sorry if i may ask a stupid question, but i havent found any solution.

    Some methods in OSA are marked as internal, when i create a class inherited from OSA (in a different assembly), how the subclass use these methods correctly?
    e.g:

    Code (CSharp):
    1. internal IList<TItemViewsHolder> CreateBufferredRecycleableItems(int count, int indexToPass = -1)
     
  21. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Hey,
    That's not a stupid question. You raised a valid concern. I'll address issues like these in the next update.
    You can safely change the OSA's source code and have them declared as protected instead. This will be compatible with the next update.

    -- Lucian
     
  22. thetnswe

    thetnswe

    Joined:
    Aug 23, 2010
    Posts:
    46
    Do you have any plans to support for new Unity's UIToolkit for scrollviews?
     
  23. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Hey,

    It's still very early to even begin to think about it. Once UIToolkit (or whatever name it'll have, since Unity is known to prefer reinventing systems instead of improving existing ones) matures enough, and if they'll responsibly provide at least the same UI elements equivalent for each type of element in uGUI, we'll look into it.

    -- Lucian
     
  24. waldgeist

    waldgeist

    Joined:
    May 6, 2017
    Posts:
    386
    Trying to get an endless spinner work. Two things I ran into:

    1. The sample code uses root.GetComponentAtPath(), but this returned null until I actually used root.GetComponentAtPath<T>() instead.

    2. When activating the "Loop Items" flag, I got this cryptic exception:

    OSAException: ChangeItemsCountInternal: At the moment, only ItemCountChangeMode.RESET is supported when looping. Use ResetItems()

    Erm, what do you mean? What the heck is ItemCountChangeMode, and where do I have to call ResetItems()?

    I couldn't find anything in the docs that explained this. In general, I am dearly missing an API documentation for this asset.
     
  25. waldgeist

    waldgeist

    Joined:
    May 6, 2017
    Posts:
    386
    Ok, I think I kinda understand what your code is trying to tell me. I guess the scroll view does not support adding single items to the list, if loop mode is enabled. I now changed the sample code to "reset" (why not just "set"?) the items instead. There is no exception being thrown anymore, but the spinner won't loop either. :-/

    I also noticed that in the sample "looping spinner" the spinning wheels sometimes get stuck if you scroll down on macOS using the Magic Mouse. Scrolling up works. What might cause this?

    EDIT: Ok, the looping problem is directly related to the "stuck wheel" problem. If you scroll down very fast to see the looping effect on a large number of items, the spinner gets stuck almost every time. This actually happens in both directions, up and down. I have 500 items (just TextMeshPro texts) in my sample. The spinner stops at around #490 +/- 5, in both the upwards and the downwards direction. I tried to modify the scroll sensitivity, but this won't change anything. The scroll is as fast as before.
     
    Last edited: Dec 15, 2020
  26. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Hey,

    1. Not sure why GetComponentAtPath would give you different results. One possibility may be that the variable that you pass to it is not of the same type as the actual component (the T you're using in the second way).
    Didn't encounter this issue until now, so it must be a variation of the above.
    2. [resolved]

    You may find answers to your questions in the FAQ section in the main manual. Lately, 70% of the questions find their answer there. For this case, search for "How to make looping work when there are fewer items than what can be shown in viewport".

    As for the mouse issue, thanks for being specific about the devices - this always helps -, but I'm pretty sure it'd happen on any platform. Do you have a chance to test the same scene on a Windows machine?
    But first make sure this issue is not directly derived from #2, as it kinda seems to be the case
     
  27. waldgeist

    waldgeist

    Joined:
    May 6, 2017
    Posts:
    386
    It actually was (TMP_Text).

    I read this, but I don't understand why this would apply to my case? I actually tried 500 items which exceed the viewport by far (only 1.5 items fit into it). But still, if I scroll fast, the spinner gets stuck at either the bottom or top of the items. This is also the case in your sample scene which I assume has been setup correctly.
     
  28. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    I guess I need to buy a mouse to test this. :D
    Hm, maybe there's a way to simulate the mouse input in unity. I see a long road ahead in debugging this. Could you log "Input.mouseScrollDelta" somewhere in an Update() while doing the scroll gesture and screen-record it? I might be able to simulate that on my machine or at least have a clue about what could cause the issue.
     
  29. sp-gillian

    sp-gillian

    Joined:
    May 8, 2019
    Posts:
    12
    Hey! I'm having an issue implementing my version of the "Chat" example and was hoping for some pointers.

    1st issue: The "images" in my chat are loaded via addressables, so aren't available immediately. This means that my message bubbles require a resize once the load is complete, I thought I'd be smart and trigger an event that calls ScheduleComputeVisibilityTwinPass an extra time, but OSA is very upset about this because it's not called from within UpdateViewsHolder or OnItemIndexChangedDueInsertOrRemove. Is there an alternative way of notifying the ListAdapter that the size of its contents may have changed?
     
  30. sp-gillian

    sp-gillian

    Joined:
    May 8, 2019
    Posts:
    12
    Good news: I can answer my own question :) I just found ForceRebuildViewsHolderAndUpdateSize, which is exactly what I wanted.
     
  31. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Yes.
     
  32. waldgeist

    waldgeist

    Joined:
    May 6, 2017
    Posts:
    386
    Phew, this documentation (or the lack of) is driving me crazy. :-/

    What I am trying to achieve: I need an endless (looping) spinner that has 5 visible items max, centered to the middle. However, the number of items may be lower than 5 items.

    If less than 5 items are visible, the wheel should shrink accordingly. Or, as a prio 2 alternative, it could keep the 5 items height, but at least allow me to navigate to one of the items that are visible (by dragging the items so one of them is in the middle of the spinner).

    I saw section 4 in the docs, but solution 1 (duplicating the model) doesn't make sense in my case, and if I try solution 2 (disabling looping) the spinner won't move anymore, i.e. I can't drag an item to the center of the viewport to select it.

    Thus, I tried to adjust the size of the scroll view port depending on its content (by setting the sizeDelta of its Rect transform), but this made me run into an

    'OSA' reports a zero or negative height

    error. I wondered how I might tell OSA to recalculate its internal data structures if the viewport size changes and found ScheduleForceRebuildLayout(), but calling this didn't help.

    I am *dearly* missing an API documentation that explains each and every method in detail, as well as an explanation of all the OSA params. Yes, there are sample scenes, but their setup is pretty complicated, so it's hard to understand them as well.
     
  33. jmansa

    jmansa

    Joined:
    Apr 13, 2012
    Posts:
    75
    Trying to create a facebook-like wall/story, but am having some issues with the contentsizefitter!?! See my video example here:



    As you can see, when a model is loaded, It time from time resizes after it is loaded into the scrollview/screen... How can i make this happen before it is visible?!?

    Here is my code fro the OSA:
    Code (CSharp):
    1. public class WallAdapter : OSA<MyParams, MyLWallItemViewsHolder>
    2. {
    3.     // LETS GET DATA FROM DATABASE //
    4.  
    5.     public int loadFrom = 0;
    6.     public int loadInterval = 10;
    7.  
    8.     public bool _Fetching;
    9.     public bool _LoadedAll;
    10.     public bool _waitingForGroup;
    11.  
    12.     public string thisGroupId;
    13.  
    14.     public SimpleDataHelper<WallStory> Data { get; private set; }
    15.  
    16.     #region OSA implementation
    17.     protected override void Start()
    18.     {
    19.         Data = new SimpleDataHelper<WallStory>(this);
    20.         base.Start();
    21.     }
    22.  
    23.     protected override void Update()
    24.     {
    25.         base.Update();
    26.  
    27.         if (!IsInitialized)
    28.             return;
    29.  
    30.         if (_Fetching)
    31.             return;
    32.  
    33.         if (_waitingForGroup)
    34.             return;
    35.  
    36.         if (_LoadedAll)
    37.             return;
    38.  
    39.         int lastVisisbleItemIndex = -1;
    40.  
    41.         if (VisibleItemsCount > 0)
    42.             lastVisisbleItemIndex = GetItemViewsHolder(VisibleItemsCount - 1).ItemIndex;
    43.  
    44.         int numberOfItemsBelowLastVisible = Data.Count - (lastVisisbleItemIndex + 1);
    45.  
    46.         // If the number of items available below the last visible (i.e. the bottom-most one, in our case) is less than <adapterParams.preFetchedItemsCount>, get more
    47.         if (numberOfItemsBelowLastVisible < _Params.preFetchedItemsCount)
    48.         {
    49.             int newPotentialNumberOfItems = Data.Count + _Params.preFetchedItemsCount;
    50.  
    51.             if (newPotentialNumberOfItems > Data.Count) // i.e. if we there's enough room for at least 1 more item
    52.             {
    53.                 StartPreFetching();
    54.             }
    55.         }
    56.     }
    57.  
    58.     public void ResetWall()
    59.     {
    60.         ResetItems(0);
    61.         Data = new SimpleDataHelper<WallStory>(this);
    62.         loadFrom = 0;
    63.     }
    64.  
    65.     public void StartPreFetching()
    66.     {
    67.         _Fetching = true;
    68.         DataGetWall.GetWallstories(thisGroupId, loadFrom, loadInterval);
    69.     }
    70.  
    71.    
    72.     protected override MyLWallItemViewsHolder CreateViewsHolder(int itemIndex)
    73.     {
    74.         var instance = new MyLWallItemViewsHolder();
    75.  
    76.         instance.Init(_Params.ItemPrefab, _Params.Content, itemIndex);
    77.  
    78.         return instance;
    79.     }
    80.  
    81.     protected override void OnItemHeightChangedPreTwinPass(MyLWallItemViewsHolder viewsHolder)
    82.     {
    83.         base.OnItemHeightChangedPreTwinPass(viewsHolder);
    84.  
    85.         Data[viewsHolder.ItemIndex].HasPendingSizeChange = false;
    86.     }
    87.  
    88.     protected override void UpdateViewsHolder(MyLWallItemViewsHolder newOrRecycled)
    89.     {
    90.  
    91.         WallStory model = Data[newOrRecycled.ItemIndex];
    92.  
    93.         ScheduleComputeVisibilityTwinPass(_Params.freezeContentEndEdgeOnCountChange);
    94.  
    95.         newOrRecycled.wallItemComponent.authorName.text = model.author;
    96.         newOrRecycled.wallItemComponent.content.text = model.story;
    97.         newOrRecycled.wallItemComponent.comments.text = model.comments.Count +" comments";
    98.         newOrRecycled.wallItemComponent.likes.text = model.likes.Count.ToString();
    99.  
    100.         newOrRecycled.wallItemComponent.picturePanel.SetActive(false);
    101.  
    102.         if (model.pictures.Count > 0)
    103.         {
    104.             if (model.mediaHeight > 0)
    105.             {
    106.                 newOrRecycled.wallItemComponent.loading.gameObject.SetActive(true);
    107.                 newOrRecycled.wallItemComponent.picturePanel.GetComponent<RectTransform>().sizeDelta = new Vector2(640f, model.mediaHeight);
    108.                 newOrRecycled.wallItemComponent.picturePanel.SetActive(true);
    109.  
    110.                 MediaManager.LoadWallImage(model.pictures[0], newOrRecycled.wallItemComponent.pictureContainer, null);
    111.             }
    112.         }
    113.         else
    114.         {
    115.             newOrRecycled.wallItemComponent.loading.gameObject.SetActive(false);
    116.             newOrRecycled.wallItemComponent.picturePanel.SetActive(false);
    117.         }
    118.  
    119.         if (model.HasPendingSizeChange)
    120.         {
    121.             newOrRecycled.MarkForRebuild();
    122.             ScheduleComputeVisibilityTwinPass(true);
    123.         }
    124.  
    125.     }
    126.  
    127.     protected override void OnBeforeRecycleOrDisableViewsHolder(MyLWallItemViewsHolder inRecycleBinOrVisible, int newItemIndex)
    128.     {
    129.         base.OnBeforeRecycleOrDisableViewsHolder(inRecycleBinOrVisible, newItemIndex);
    130.     }
    131.  
    132.     protected override void RebuildLayoutDueToScrollViewSizeChange()
    133.     {
    134.         SetAllModelsHavePendingSizeChange();
    135.  
    136.         base.RebuildLayoutDueToScrollViewSizeChange();
    137.     }
    138.  
    139.     #endregion
    140.  
    141.     #region data manipulation
    142.     public void AddItemsAt(int index, IList<WallStory> items)
    143.     {
    144.         Data.InsertItems(index, items);
    145.     }
    146.  
    147.     public void RemoveItemsFrom(int index, int count)
    148.     {
    149.         Data.RemoveItems(index, count);
    150.     }
    151.  
    152.     public void SetItems(IList<WallStory> items)
    153.     {
    154.         Data.ResetItems(items);
    155.     }
    156.     #endregion
    157.  
    158.     public List<WallStory> newStoriesList = new List<WallStory>();
    159.  
    160.  
    161.     public void onGetWallStoriesResponse(object sender, ResponseData data)
    162.     {
    163.  
    164.         Dictionary<string, WallStory> newStories = data.data[0] as Dictionary<string, WallStory>;
    165.  
    166.         loadFrom += newStories.Count;
    167.  
    168.  
    169.         if (newStories.Count > 0)
    170.         {
    171.  
    172.             foreach (KeyValuePair<string, WallStory> entry in newStories)
    173.             {
    174.  
    175.                 newStoriesList.Add(entry.Value);
    176.             }
    177.  
    178.         }
    179.  
    180.         var newItems = new WallStory[newStoriesList.Count];
    181.  
    182.         for (int i = 0; i < newStoriesList.Count; ++i)
    183.         {
    184.             newItems[i] = newStoriesList[i];
    185.         }
    186.  
    187.         _Fetching = false;
    188.  
    189.         OnDataRetrieved(newItems);
    190.     }
    191.  
    192.  
    193.     void OnDataRetrieved(WallStory[] newItems)
    194.     {
    195.         Data.InsertItemsAtEnd(newItems);
    196.     }
    197.  
    198.     void SetAllModelsHavePendingSizeChange()
    199.     {
    200.         foreach (var model in Data)
    201.         {
    202.             model.HasPendingSizeChange = true;
    203.         }
    204.     }
    205. }
    206.  
    207. [Serializable]
    208. public class MyParams : BaseParamsWithPrefab
    209. {
    210.     public int preFetchedItemsCount;
    211.     [Tooltip("Set to -1 if while fetching <preFetchedItemsCount> items, the adapter shouldn't check for a capacity limit")]
    212.     public int totalCapacity;
    213.  
    214.     [NonSerialized]
    215.     public bool freezeContentEndEdgeOnCountChange;
    216. }
    217.  
    218.  
    219. // This class keeps references to an item's views.
    220. // Your views holder should extend BaseItemViewsHolder for ListViews and CellViewsHolder for GridViews
    221. public class MyLWallItemViewsHolder : BaseItemViewsHolder
    222. {
    223.  
    224.     public WallItemComponent wallItemComponent;
    225.  
    226.     ContentSizeFitter CSF { get; set; }
    227.  
    228.  
    229.     // Retrieving the views from the item's root GameObject
    230.     public override void CollectViews()
    231.     {
    232.         base.CollectViews();
    233.  
    234.         wallItemComponent = root.GetComponent<WallItemComponent>();
    235.  
    236.         CSF = root.GetComponent<ContentSizeFitter>();
    237.         CSF.enabled = false;
    238.  
    239.     }
    240.  
    241.     public override void MarkForRebuild()
    242.     {
    243.         base.MarkForRebuild();
    244.  
    245.         if (CSF)
    246.         {
    247.             CSF.enabled = true;
    248.         }
    249.     }
    250.  
    251.     public override void UnmarkForRebuild()
    252.     {
    253.         if (CSF)
    254.         {
    255.             CSF.enabled = false;
    256.         }
    257.         base.UnmarkForRebuild();
    258.     }
    259.  
    260. }
    Really hope someone can help me with this, since I am getting crazy and running out of ideas :-/

    Thanks in advance :)
     
  34. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    The code already documents all the important members of the classes and the classes themselves. An online documentation would just duplicate that info. We had an online API ref, but it was more useful when OSA was closed-source, because you couldn't see what's in the DLLs.
    In your particular case, if duplicating the models is not what you want, I cannot see how you'd want anything else other than having a disabled scroll/drag. Though you seem to need to have the item centered, which can be achieved by setting the Gravity property to center (inspector or code). This controls what happens if your content size is smaller than the viewport. Was this the missing puzzle piece?
     
  35. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Hello,

    The "_Params.freezeContentEndEdgeOnCountChange" is a value that you need to decide. In the demos is provided in the params just to be able to switch it via a toggle and see its effects in real-time. When you see this parameter in the methods, know that it refers to where should the content be pushed, towards the beginning or towards the ending (the respective methods also describe this). Usually, when the starting position is TOP or LEFT, you have it on false; if you start from BOTTOM (like in a chat) or RIGHT, you set it to true, so that the content is pushed up when adding new items.

    Also, you don't need to call ScheduleComputeVisibilityTwinPass twice.
    Also newOrRecycled.MarkForRebuild is called for you, by OSA, inside the "twin pass", which is not immediate, but after all currently visible items had UpdateViewsHolder called upon them. So you don't need to manually call it (who knows, maybe this is what's causing some of the issues).

    Hope this helps
     
  36. waldgeist

    waldgeist

    Joined:
    May 6, 2017
    Posts:
    386
    I disagree. Documentation embedded in the code is very hard to read, and it tends to be pretty scarce and technical, which is also the case here. Just as an example: There's a function called "OnItemHeightChangedPreTwinPass". The description says: "Only called for vertical ScrollRects. Called just before a "Twin" ComputeVisibility will execute. This can be used, for example, to disable a ContentSizeFitter on the item which was used to externally calculate the item's size in the current Twin ComputeVisibility pass". Okay, but what a "TwinPass" actually is, is an excercise left to the reader. At least I couldn't find any documentation about what "passes" this code is using. Any kind of "lifecycle" explanations would be very helpful to understand how the whole process works and where you can actually hook into it.

    Besides, readability even gets worse if the code actually includes meta tags for a doc generator. I mean, do you really consider this as being "readable":



    Ok, I made a quick video to explain this, that's better than trying to put it in words:



    In the first part, I have 6 items in the wheel and everything works as I need it. In the second section, there are only three items. Those are shown correctly, but the user can't drag them to put them in the center. In the last part, there are only 2 items. In this case, I would love to still be able to snap the wheel so one item is seen in the center (using your snap script), but this doesn't work either.

    To resolve this, I could create dummy (empty items) above and below, but I don't want the user to be able to select these. So at least I would need a feature that allows me to "stop" the wheel somehow once the user has scrolled it to the last item that actually has visible content.

    The other (and even better) solution would be to shrink the wheel itself, so it resizes according to the number of items being shown. But when I tried this (by adjusting the scrollrect of the OSA's container element), it threw the aforementioned errors. I am missing a sample where the scroll container size changes in the direction of the scroll. All samples I saw change the container size in the orthogonal direction.
     
    Last edited: Dec 21, 2020
  37. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Noted. As of next release, the online reference will be back online once again. Meanwhile, just hover over the class and you'll see the formatted summarry (without the tags like "para", "see" etc.).

    That's perfect. Thanks. So in this case, you need to use OSAPercentageContentPaddingSetter. The default params will do.
    Now, do you dynamically change the number of items for the spinner or does it have a constant number of items?

    In the second case, you'd just use OSAPercentageContentPaddingSetter (with looping disabled) for lists of just a few items, and keep your current implementation for large enough lists.

    In the first case, you need to:
    1. Event: list becomes "large" enough for looping:
    => Destroy the OSAPercentageContentPaddingSetter component, if any. Set LoopItems=true. Call Params.InitIfNeeded().
    2. Event: list becomes too "small" for looping:
    => Set LoopItems=false. Add the OSAPercentageContentPaddingSetter component on OSA. Params.InitIfNeeded().

    If on either event #1 or #2 you get unexpected results, a Refresh() should fix them;
    if not, a ResetItems(0) followed by a ResetItems(<itemsCount>); (*)
    if not, a ScheduleForceRebuildLayout() should do it (in the next frame); (**)
    if not, do both (*) and (**), in this order.

    As for how to know whether you can safely loop or not, you can do it in multiple ways. For ex, inside OSA, use this.GetScrollableArea() (extension method) and that should be at least <itemSize+spacing>, but use <2 *itemSize> just in case.
    If you're curious, the scrollable area is basically <contentSize-viewportSize>. As for itemSize, you're probably using the Params.DefaultItemSize, so that's the value to insert there.

    Hope this helps
     
    Last edited: Dec 21, 2020
  38. faolad

    faolad

    Joined:
    Jan 27, 2013
    Posts:
    118
    Is there a way to use input(up/down) to navigate through the scroll list?
     
  39. waldgeist

    waldgeist

    Joined:
    May 6, 2017
    Posts:
    386
    Awesome, thanks!

    I know, but this is still pretty cumbersome to read, especially if you want to get the whole picture.

    They change dynamically. Actually, they can shrink and grow, as they are based on other conditions.

    I tried adding the setter like this:

    Code (CSharp):
    1.     void OnDataRetrieved(Layer[] newItems) {
    2.         var loopItems = newItems.Length >= maxItems;
    3.         this._Params.effects.LoopItems = loopItems;
    4.         if (loopItems) {
    5.             if (percentagePaddingSetter != null) Destroy(percentagePaddingSetter);
    6.         } else {
    7.             if (percentagePaddingSetter == null) percentagePaddingSetter = gameObject.AddComponent<OSAPercentageContentPaddingSetter>();
    8.         }
    9.         SetItems(newItems);
    10.         _Params.InitIfNeeded(this);
    11.         Refresh();
    12.         SmoothScrollTo(0, 0.5f);
    13.     }
    But this did not work. If I have 3 items, for instance, the setter shows up on the OSA game object, but I still can't spin the wheel, just like in the second section of the video above. Maybe I got something wrong? The setter script docs won't tell you on which game object you have to place it. Maybe the OSA is not the right one? Do you have samples that use these additional components?
     
    Last edited: Dec 24, 2020
  40. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Yes, let's get in touch on discord about this. It depends on the complexity of your need. Also, I've answered a similar request yesterday - if it was you, ignore this message
     
  41. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    If SetItems(newItems) submits the new item lists to OSA, call it after Params.InitIfNeeded(). And only call Refresh() if it doesn't work without it.

    The actions aren't executed in the same order as I wrote them, but forget that. I just realized, for this simple case, changing the padding manually looks cleaner:
    Params.ContentPadding.top = Params.ContentPadding.bottom = (int)(GetViewportSize() / 2);
    Execute this instead of adding the padding setter component (which is more suited for setting a constant padding at edit-time).
    And in place of destroying the setter component, set the top/bottom paddings to Params.ContentSpacing (it'll automatically be set for you in InitIfNeeded() when LoopItems becomes true, but it shows you a log message, so by doing this in advance you avoid that).

    Edit: Also, if you expect the OSA's or its viewport's size to change at runtime, override PostRebuildLayoutDueToScrollViewSizeChange and re-calculate your maxItems. This, of course, should trigger the same code as the count changes (since the number of items that can fit in the viewport might've been changed as a result of this)
     
    Last edited: Dec 23, 2020
  42. waldgeist

    waldgeist

    Joined:
    May 6, 2017
    Posts:
    386
    I may be too dumb, but this approach doesn't work either. Here's my code:

    Code (CSharp):
    1.     void OnDataRetrieved(Layer[] newItems) {
    2.         var loopItems = newItems.Length >= maxItems;
    3.         this._Params.effects.LoopItems = loopItems;
    4.         if (loopItems) {
    5.             _Params.ContentPadding.top = _Params.ContentPadding.bottom = (int) _Params.ContentSpacing;
    6.         } else {
    7.             _Params.ContentPadding.top = _Params.ContentPadding.bottom = (int) (GetViewportSize() / 2);
    8.         }
    9.         _Params.InitIfNeeded(this);
    10.         SetItems(newItems);
    11.         // Refresh();
    12.         SmoothScrollTo(0, 0.5f);
    13.     }
    14.  
    It still behaves like before. If there is less than 5 items, I can't move the wheel at all (like in the 2nd section of my video). And if its an even count (like in the 3rd part of my video with 2 items), the wheel is centered in the middle of 2 items. I also do not understand how padding the content should change this? The basic problem is that the wheel is not movable, because the code thinks there is no need to do that, I guess.

    Maybe instead of explaining, it would be easier to update the "looping_spinner" sample scene, so you can actually go down to less than 4 items? This would be exactly what I need.
     
    Last edited: Dec 25, 2020
  43. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Right. For OSA to take the padding changes into account, you need a ScheduleForceRebuildLayout() call.
    Changing the looping spinner example was a good idea, so other users will also know about this.
    I've improved the code and it'll make it into the next version. At the current moment, these are the additions:

    Code (CSharp):
    1.         /// <summary>If the viewport's size changes, looping capability may also change</summary>
    2.         protected override void PostRebuildLayoutDueToScrollViewSizeChange()
    3.         {
    4.             base.PostRebuildLayoutDueToScrollViewSizeChange();
    5.             ManageLoopingOnItemCountOrLayoutChanges(GetItemsCount());
    6.         }
    7.  
    8.         /// <summary>If the items count changes, looping capability may also change</summary>
    9.         public void ChangeItemsCountWithChecks(int newCount)
    10.         {
    11.             ResetItems(newCount);
    12.             ManageLoopingOnItemCountOrLayoutChanges(newCount);
    13.         }
    14.  
    15.         void ManageLoopingOnItemCountOrLayoutChanges(int newItemsCount)
    16.         {
    17.             bool wasLooping = _Params.effects.LoopItems;
    18.             bool canLoop = CanLoop(newItemsCount);
    19.             if (wasLooping == canLoop)
    20.                 return;
    21.  
    22.             _Params.effects.LoopItems = canLoop;
    23.             double padStartEnd;
    24.             if (canLoop)
    25.                 padStartEnd = _Params.ContentSpacing;
    26.             else
    27.                 // Explanation: Having half the viewport as padding allows you to move the item at extremity
    28.                 // so that its EDGE will be exactly at the viewport's center. After that, we subtract half of the
    29.                 // item's size from the padding, so that the item's CENTER will be in the viewport's center.
    30.                 padStartEnd = GetViewportSize() / 2 - _Params.DefaultItemSize / 2;
    31.  
    32.             _Params.ContentPadding.top = _Params.ContentPadding.bottom = (int)padStartEnd;
    33.             ScheduleForceRebuildLayout();
    34.         }
    35.  
    36.         bool CanLoop(int newItemsCount)
    37.         {
    38.             float itemSizeAndSpacing = _Params.DefaultItemSize + _Params.ContentSpacing;
    39.             int minCount = (int)(GetViewportSize() / itemSizeAndSpacing + 2);
    40.             return newItemsCount >= minCount;
    41.         }
    I've also updated the way it retrieves the middle item (to color it), if you're interested in that. Now, it re-uses the work done by the Snapper script, which is also more accurate:
    _Params.Snapper.GetMiddleVH(out float _)
     
  44. waldgeist

    waldgeist

    Joined:
    May 6, 2017
    Posts:
    386
    Thanks, will have a look at it!
     
  45. AGeorgy

    AGeorgy

    Joined:
    Sep 16, 2013
    Posts:
    42
    Is there the best way to solve a problem?
    Code (CSharp):
    1. public class ListAdapter : OSA<Params, AViewsHolder>
    2.     {
    3.         private SimpleDataHelper<AModel> _data;
    4.         private Config _config;
    5.  
    6.         public void Initialize(Config config)
    7.         {
    8.             _config = config;
    9.             _data = new SimpleDataHelper<AModel>(this);
    10.  
    11.             if (!IsInitialized)
    12.                 Init();
    13.             else if(_data.Count > 0)
    14.             {
    15.                 ResetItems(_data.Count);
    16.             }
    17.         }
    18.  
    19.         public void SetSection(int index)
    20.         {
    21.             var sectionConfig = _config.Sections[index];
    22.             SetData(sectionConfig.GetModels());
    23.         }
    24.  
    25.         private async void SetData(IList<AModel> data)
    26.         {
    27.             _data.ResetItems(data);
    28.             SetNormalizedPosition(1d);
    29.             await Awaiters.EndOfFrame;
    30.             SetNormalizedPosition(1d); // Workaround to fix, the scroll doesn't move to the very beginning
    31.         }
    32.  
    33.         protected override AViewsHolder CreateViewsHolder(int itemIndex)
    34.         {
    35.             var modelType = _data[itemIndex].CachedType;
    36.             if (modelType == typeof(AdviceModel))
    37.             {
    38.                 var vh = new AdviceViewsHolder();
    39.                 vh.Init(_Params.AdvicePrefab, _Params.Content, itemIndex);
    40.                 return vh;
    41.             }
    42.             if (modelType == typeof(ArmorModel))
    43.             {
    44.                 var vh = new ArmorViewsHolder();
    45.                 vh.Init(_Params.ArmorPrefab, _Params.Content, itemIndex);
    46.                 return vh;
    47.             }
    48.             if (modelType == typeof(BasicsModel))
    49.             {
    50.                 var vh = new BasicsViewsHolder();
    51.                 vh.Init(_Params.BasicsPrefab, _Params.Content, itemIndex);
    52.                 return vh;
    53.             }
    54.             if (modelType == typeof(ItemLootModel) || modelType == typeof(ConsumableLootModel))
    55.             {
    56.                 var vh = new LootViewsHolder();
    57.                 vh.Init(_Params.LootArticleView, _Params.Content, itemIndex);
    58.                 return vh;
    59.             }
    60.             if (modelType == typeof(SeparatorModel))
    61.             {
    62.                 var vh = new SeparatorViewsHolder();
    63.                 vh.Init(_Params.SeparatorPrefab, _Params.Content, itemIndex);
    64.                 return vh;
    65.             }
    66.  
    67.             DebugLogger.Instance.LogWarning(nameof(ListAdapter), $"Unrecognized model type: {modelType.Name}");
    68.             return null;
    69.         }
    70.  
    71.         protected override void UpdateViewsHolder(AViewsHolder newOrRecycled)
    72.         {
    73.             var model = _data[newOrRecycled.ItemIndex];
    74.             newOrRecycled.UpdateView(model);
    75.  
    76.             if (model.HasPendingSizeChange)
    77.             {
    78.                 // Height will be available before the next 'twin' pass, inside OnItemHeightChangedPreTwinPass() callback (see above)
    79.                 ScheduleComputeVisibilityTwinPass(true);
    80.             }
    81.         }
    82.  
    83.         protected override void OnItemHeightChangedPreTwinPass(AViewsHolder vh)
    84.         {
    85.             base.OnItemHeightChangedPreTwinPass(vh);
    86.             var model = _data[vh.ItemIndex];
    87.             model.HasPendingSizeChange = false;
    88.         }
    89.  
    90.         protected override bool IsRecyclable(AViewsHolder potentiallyRecyclable, int indexOfItemThatWillBecomeVisible, double sizeOfItemThatWillBecomeVisible)
    91.         {
    92.             var model = _data[indexOfItemThatWillBecomeVisible];
    93.             return potentiallyRecyclable.CanPresentModelType(model.CachedType);
    94.         }
    95.     }
    Every time I change a section (SetSection), I have to call SetNormalizedPosition twice. To have scroll as fresh new. And moreover, it seems like scroll populating at the current position and then moves up.
    https://1drv.ms/u/s!ArMIt3CgRRfQrfUXlXTIDdDBVabHig?e=DKMeG8

     
    Last edited: Feb 15, 2021
  46. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Hey,

    Sorry for the late reply (in the process of moving to a new office).

    It seems the links to the images are broken, so I'm not sure I can understand the problem just from code.

    Also, I haven't used async/await functionality in C# too much, although I've worked on several projects and frameworks (it just wasn't necessary). I used UniTask for some very specific workloads, but in a UI component where we're more event-driven, I'm not able to comprehend the whole impact async-await could have on OSA's lifecycle & the assumptions it makes. Thus, I cannot guarantee stability when using async/await. If you have more experience with them and would be willing to give me a detailed insight into them, please write me on discord.

    Otherwise, your CSF implementation seems ok. One exception is: you probably want ScheduleComputeVisibilityTwinPass(endStationary=false) because you want the top edge of the content to be stationary when resizing items (resizing is automatically via CSF), not the bottom (as used in chat-based scroll views).

    Also, if async/await works flawlessly for you (in general with OSA), and the above fix doesn't work, you probably don't need to call SetNormalizedPosition(1d) twice, but only after having reset the items.

    -- Lucian
     
  47. chenquanjun

    chenquanjun

    Joined:
    Aug 23, 2017
    Posts:
    5
    change grid view cell instantiate into virtual method, so cell instance can be created outside.
     

    Attached Files:

    • osa1.png
      osa1.png
      File size:
      67.9 KB
      Views:
      327
  48. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Hey, good suggestion. Thanks!
    Will have it in next versions
     
  49. chenquanjun

    chenquanjun

    Joined:
    Aug 23, 2017
    Posts:
    5
    Snapping8 not work when using screen-space-camera canvas and orthographic camera.
    1.Open datetime_picker demo scene
    2.Change camera projection to Orthographic and size to 1
    3.Change Canvas to Screen Space Camera and drag Main Camera to render Camera
    run scene and scroll date item, it won't snapping to center
     

    Attached Files:

    xucian likes this.
  50. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    836
    Thanks. Will investigate this as part of our next release. If this is urgent, please email me (and also add your invoice number to it).

    -- Lucian