Author Topic: How to use UIWrapContent programatically?  (Read 16819 times)

N3uRo

  • Guest
How to use UIWrapContent programatically?
« on: June 27, 2014, 11:18:59 AM »
I see that there is a virtual method that receives "Transform item, int index".

Having a List of data how can I use this? I don't know how to associate data to that index because that method only triggers when there is a new item visible.

Can you post a quick example?

Imagine that I have a List of strings containing letters from A to Z and I want to bind this to my list.

How can I achieve that?

Thanks!

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: How to use UIWrapContent programatically?
« Reply #1 on: June 27, 2014, 02:02:40 PM »
When an item gets repositioned, that function is called so that you can re-populate the item's data using its new location in the list. Its ideal usage would be if you have something like 10 entries but want to cover 1000 different values, or something similarly extreme where it pays to only have a few elements at a time.

N3uRo

  • Guest
Re: How to use UIWrapContent programatically?
« Reply #2 on: June 28, 2014, 10:54:09 AM »
i know the purppose of that script, what i need its an example of it as i mention it in  my first post. Can you give that? It would be useful for ngui docs section as well

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: How to use UIWrapContent programatically?
« Reply #3 on: June 28, 2014, 04:56:01 PM »
1. Create a custom script:
  1. using UnityEngine;
  2.  
  3. public class MyCustomWrapper : UIWrapContent
  4. {
  5.         protected override void UpdateItem (Transform item, int index)
  6.         {
  7.                 // The ".x" assumes the scroll view is horizontal. if vertical, use ".y"
  8.                 int realIndex = Mathf.RoundToInt(item.localPosition.x / itemSize);
  9.                 item.GetComponent<UILabel>().text = realIndex.ToString();
  10.                 base.UpdateItem(item, index);
  11.         }
  12. }
2. Create a scroll view.
3. Create a child under the scroll view and attach MyCustomWrapper to it.
4. Create a label child under the wrapper object, resize it to 100x100, give it a box collider and UIDragScrollView.
5. Duplicate the label a couple of times then right-click the wrap script, sort alphabetically.

Hit Play, drag the scroll view around. You will see that the label text will change when they get wrapped. You can easily improve this further by changing the content as you see fit.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: How to use UIWrapContent programatically?
« Reply #4 on: June 28, 2014, 05:14:09 PM »
P.S. I've thought about it a bit and simplified it further for the next update. I've added a delegate to make the process easier so you don't have to derive from the wrap content script, and the "real index" is calculated for you automatically. The previous example I posted will work just fine as well.
  1. using UnityEngine;
  2.  
  3. public class Test : MonoBehaviour
  4. {
  5.         void Awake()
  6.         {
  7.                 GetComponent<UIWrapContent>().onInitializeItem = SetItemData;
  8.         }
  9.  
  10.         void SetItemData (GameObject go, int wrapIndex, int realIndex)
  11.         {
  12.                 go.GetComponent<UILabel>().text = realIndex.ToString();
  13.         }
  14. }

bdominguez

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 38
    • View Profile
Re: How to use UIWrapContent programatically?
« Reply #5 on: June 30, 2014, 04:35:25 AM »
I have two problems:

1. It doesn't work when you use a list with a UIGrid with a PIVOT in TOP (each element has it's pivot in top also). When it removes one item from bottom an puts on top it does later.

  1. float distance = t.localPosition.y - center.y;
  2.  

It expects that localPosition.y it's the center but sometimes it's not.

2. realIndex variable should be other thing.

See this:

http://developer.android.com/reference/android/widget/ArrayAdapter.html

It's a class where you provide your data and it uses with the list (MVC).

Constructor:

  1. ArrayAdapter(Context context, int resource, T[] objects)
  2.  

Method:

  1. getView(int position, View convertView, ViewGroup parent)
  2.  

Where you override it and you fill your data. "int position" it's the index within your data list.

Suppose you have a list of 3 items, indexes should be values like:

0
1
2
0
1
2

And not:

0
1
2
3
4
5

Or:

-5
-4
-3
-2
-1

So for that case you should provide something to tell how many items you have. Here you have the code i used to achieve this:

  1. int index = 0;
  2.  
  3.         if (realIndex > 0) {
  4.             int loop = 1 + (realIndex / count);
  5.             index = (loop * count) - realIndex;
  6.             index = index == count ? 0 : index;
  7.         } else {
  8.             index = -realIndex % count;
  9.         }
  10.  

bdominguez

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 38
    • View Profile
Re: How to use UIWrapContent programatically?
« Reply #6 on: June 30, 2014, 04:41:56 AM »
Also there is no way to disable this script. Because of this:

  1. mScroll.GetComponent<UIPanel>().onClipMove = OnMove;
  2.  

There should be a way to disable it because if you have dynamic contents you can have 100 elements or 5. When you have 5 you don't want a this behaviour because it's within panel.

And this is no way to tell to not include inactive items. I had to change it:

  1. mChildren.Clear();
  2.         for (int i = 0; i < mTrans.childCount; ++i) {
  3.             if (hideInactive && !mTrans.GetChild(i).gameObject.activeSelf) continue;
  4.  

bdominguez

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 38
    • View Profile
Re: How to use UIWrapContent programatically?
« Reply #7 on: June 30, 2014, 05:07:22 AM »
You need to do this and you also should save previous "restrictWithinPanel" value set by user.

  1. void OnEnable() {
  2.         if (mScroll) {
  3.             mScroll.restrictWithinPanel = false;
  4.         }
  5.     }
  6.  
  7.     void OnDisable() {
  8.         if (mScroll) {
  9.             mScroll.restrictWithinPanel = true;
  10.         }
  11.     }
  12.  
  13.     protected override void OnMove(UIPanel panel) {
  14.         if (enabled) {
  15.             base.OnMove(panel);
  16.         }
  17.     }
  18.  

And finally it would be nice if we could "close" one end or two ends.

As you can see, this script needs some work on it.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: How to use UIWrapContent programatically?
« Reply #8 on: July 01, 2014, 03:25:54 AM »
What does UIGrid have to do with UIWrapContent? The two are very different scripts that both reposition children, and should never be used together.

You can achieve repeating indices quite easily. In the SetItemData callback just wrap the real index. You will need more items in the list than can fit into the visible space as expected (so that they wrap around properly), but what data you visualize on those items is completely up to your SetItemData callback.

UIWrapContent's purpose is not to copy the functionality of an Android control. Its purpose is to show you how you can use NGUI to create a simple unlimited scroll view. The script is 241 lines including comments, and easily derivable from, or modifiable just by setting the callback. Even if that's not good enough, the code should be short enough to be used as a template for your own custom component.

bdominguez

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 38
    • View Profile
Re: How to use UIWrapContent programatically?
« Reply #9 on: July 01, 2014, 03:32:28 AM »
What does UIGrid have to do with UIWrapContent? The two are very different scripts that both reposition children, and should never be used together.

You can achieve repeating indices quite easily. In the SetItemData callback just wrap the real index. You will need more items in the list than can fit into the visible space as expected (so that they wrap around properly), but what data you visualize on those items is completely up to your SetItemData callback.

UIWrapContent's purpose is not to copy the functionality of an Android control. Its purpose is to show you how you can use NGUI to create a simple unlimited scroll view. The script is 241 lines including comments, and easily derivable from, or modifiable just by setting the callback. Even if that's not good enough, the code should be short enough to be used as a template for your own custom component.

But if you can improve it why not do it? I have posted you code to improve it.

And it's not easily derivable from, I had to changed to protected virtual some methods and properties modifying NGUI code.

I don't understand why you are opposed to improve some scripts when your users are helping you to do it.

If you can make a better UIWrapContent script why not? I'm sure that there are more people that has the same problem and want to virtualize lists like in Android or almost any UI system.

Tripwire

  • Full Member
  • ***
  • Thank You
  • -Given: 9
  • -Receive: 0
  • Posts: 163
    • View Profile
Re: How to use UIWrapContent programatically?
« Reply #10 on: July 01, 2014, 04:20:11 AM »
But if you can improve it why not do it? I have posted you code to improve it.

And it's not easily derivable from, I had to changed to protected virtual some methods and properties modifying NGUI code.

I don't understand why you are opposed to improve some scripts when your users are helping you to do it.

If you can make a better UIWrapContent script why not? I'm sure that there are more people that has the same problem and want to virtualize lists like in Android or almost any UI system.

I second this, especially when creating timelines like the one in the Facebook app for instance the UIWrapcontent @Bdominguez describes would come in handy.

@Bdominguez, can you post your UIWrapcontent script, or the script you now use to get this working properly?

bdominguez

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 1
  • Posts: 38
    • View Profile
Re: How to use UIWrapContent programatically?
« Reply #11 on: July 01, 2014, 05:59:10 AM »
Suppose you have a collection of items.

public class Test : MonoBehaviour
{

    List<string> letters = new List<string>() { "A", "B", "C", "D" };

    void Awake()
    {
        GetComponent<UIWrapContent>().onInitializeItem = SetItemData;
    }

    void SetItemData (GameObject go, int wrapIndex, int realIndex)
    {
// THIS IS THE CODE TO CALCULATE THE INTERESTING INDEX
        int index = 0;

        if (realIndex > 0) {
            int loop = 1 + (realIndex / count);
            index = (loop * count) - realIndex;
            index = index == count ? 0 : index;
        } else {
            index = -realIndex % count;
        }
// END
 
   string letter = letters[index];

   Debug.Log(letter);

        go.GetComponent<UILabel>().text = letter;
    }
}

matteo.piccioni

  • Newbie
  • *
  • Thank You
  • -Given: 2
  • -Receive: 0
  • Posts: 8
    • View Profile
Re: How to use UIWrapContent programatically?
« Reply #12 on: July 06, 2014, 10:50:39 AM »
Hello,
thanks for help us about using of UIWrapContent, that is very useful for very long list of items

Exist a way to disable the wrap of items using only the instantiate only partial content at a time feature ?
Or we have to modify the script in some way ?

Thanks

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: How to use UIWrapContent programatically?
« Reply #13 on: July 07, 2014, 01:57:02 AM »
UIWrapContent script has the "onInitializeItem" callback you can set that will be called whenever the item needs to be re-initialized, such as when an item gets repositioned. So you can have 10 items in the wrap content list, but it can easily display any number of items just by using that callback. Simply change the data of the item when you get the callback.

Tripwire

  • Full Member
  • ***
  • Thank You
  • -Given: 9
  • -Receive: 0
  • Posts: 163
    • View Profile
Re: How to use UIWrapContent programatically?
« Reply #14 on: July 07, 2014, 01:59:37 AM »
Suppose you have a collection of items.

public class Test : MonoBehaviour
{

    List<string> letters = new List<string>() { "A", "B", "C", "D" };

    void Awake()
    {
        GetComponent<UIWrapContent>().onInitializeItem = SetItemData;
    }

    void SetItemData (GameObject go, int wrapIndex, int realIndex)
    {
// THIS IS THE CODE TO CALCULATE THE INTERESTING INDEX
        int index = 0;

        if (realIndex > 0) {
            int loop = 1 + (realIndex / count);
            index = (loop * count) - realIndex;
            index = index == count ? 0 : index;
        } else {
            index = -realIndex % count;
        }
// END
 
   string letter = letters[index];

   Debug.Log(letter);

        go.GetComponent<UILabel>().text = letter;
    }
}

Thanks for the code! How do you cope with closing the list? I'm interested in using this for some kind of timeline, like the Facebook app. The Timeline has to be closed both ends, and items are added in runtime.