Search Unity

Resizing ScrollRects

Discussion in 'UGUI & TextMesh Pro' started by Bren, Feb 20, 2015.

  1. Bren

    Bren

    Joined:
    Aug 28, 2008
    Posts:
    191
    I am having a helluva time trying to get a vertical scroll list of one column of items to work. At all.

    I have a setup something like this, child of a root Canvas, Canvas Renderer, etc:

    Dialogue (Canvas Renderer, Image)
    - Scroll Rect (Scroll Rect, Mask, Image)
    - - Scroll List (the ScrollRect's Content)
    - - - List Item
    - - - - Date (Text)
    - - - - Title (Text)
    - - - - Image (Image)
    - - - - Body (Text)

    This is for a MOTD popup so the body text can vary (short to quite long).

    Ideally, the scroll list will contain multiple/variable List Items, though at the moment I would be quite happy if it could just scroll one. When the ScrollRect is set to Elastic, it won't let you scroll much at all. Really just the size of the Scroll List. When it's Unrestricted, of course it lets you scroll up/down as much as you want, including to nowhere, so this isn't ideal.

    It works when I manually set the Scroll List to be tall enough for the entire List Item's height in the editor, but this is no good for variable length texts, and/or a variable number of List Items. The Body Text height is fine -- it's height is adjusted for the content. The problem seems to be that the @#$%ing ScrollRect never understands how tall its child elements (and their child elements) are.

    I have tried a ton of things. Content Size Fitters everywhere. Layout Elements everywhere. Grid Layout Group set to one column. Vertical Layout Group. Flexible this. Preferred that. On. And on. And on.

    I am sure this should work. It SHOULD work. But I cannot seem to hit upon the magic combination that makes it work, and I am running out of chickens to sacrifice to the fickle Unity gods.

    Can someone please help? A clear explanation of the logic would be greatly appreciated. God knows the pages of docs I've read and hours of tutorial videos I've watched haven't helped a bit. It's driving me up the wall. I implemented the entire SQL backend and client side code in less time than I've wasted screwing around with this stupid ScrollRect. PLEASE HELP!!!
     
    squigglebucket likes this.
  2. Feaver1968

    Feaver1968

    Joined:
    Nov 16, 2014
    Posts:
    70
    It is a good idea to start with an empty project and start testing UI components one at a time to see what they do. Here is a little sample I just put together that might help you get started.

    I find that the scrollrect content game object needs a content size fitter and a vertical layout group when you are adding elements dynamically/resizing. I change the pivot on the rect transform to 0,1 and set anchors to stretch in both directions (bottom right option). I make the content size fitter vertical fit preferred.

    For the list items, if you are going to use items with child items like text, images (which you are), you can add a layout element to the top most list item and give it a preferred height that you would like each list item to be. You can arrange the inner items as you need and change their anchoring/layout as needed.

    Depending on how the inner items are laid out, you may need additional horizontal/vertical layoutgroups to make things resize properly.

    Hope some of this makes sense, but as for the logic behind it... I'll leave that to someone else to explain. ;)
     

    Attached Files:

  3. Bren

    Bren

    Joined:
    Aug 28, 2008
    Posts:
    191
    HI Feaver,

    I appreciate the time you've taken to reply, and I had a look at your sample package. The scroll rect was set to Clamped initially, so it wouldn't scroll at all. When I set it to Elastic, it behaved exactly as my own. Again, thanks, but it isn't helpful.
     
  4. Feaver1968

    Feaver1968

    Joined:
    Nov 16, 2014
    Posts:
    70
    Oh, I think I understand. You are expecting the scrollrect to always scroll but it doesn't. I think this is because the size of the scrollrect's content recttransform is within the bounds of the scrollrect. Basically the scrollrect just won't allow scrolling if it finds that it isn't necessary. If you open my example again and duplicate the item under content a few times, you'll see that it does scroll once the content expands (content size fitter expands it) beyond the size of the scrollrect.

    I think what you are looking for is an option to turn off 'disable scroll if fits' because you DO want to scroll even when there are few items. There could be a way to do this, but it is kind of beyond my experience
     
  5. Bren

    Bren

    Joined:
    Aug 28, 2008
    Posts:
    191
    Not exactly. I wouldn't want it to scroll if the content fit. In your example, I made text object longer, and then the scroll rect too small for the content, and it didn't work. I did not try duplicating the content.

    I very much appreciate your assistance nonetheless.

    What I ended up doing was importing the UI Samples package into my project, and meticulously recreating it. This didn't work. What mystery! Ultimately, I made a prefab of the Scrolling example, and then changed one small thing at a time, running it after every change to make sure it still worked, until I had what I wanted, or close enough. Easy peasy and only took two days! /sarcasm

    BTW What I ended up with uses no Content Size Fitters, Layout Elements, or anything like that, and works good enough.

    Incidentally, Unity, a 5 minute recital of a Component's members which doesn't actually take you through the steps to recreate what is demonstrated is not really a tutorial. Not linking to a package is not helpful. Burying the UI Samples package in the Asset Store then only mentioning it in a forum post is not helpful either.
     
  6. nygren

    nygren

    Joined:
    Feb 13, 2014
    Posts:
    33
    Since you said that it worked "good enough", I'm guessing you're not quite pleased with what you have currently. So I'll give it a shot.

    Disclaimer: I'm in no way particularly proficient in neither Unity in general or it's GUI system in particular ;)

    Hopefully you won't mind that I've used auto layout components. Since you wanted the logic behind it, I'm also deliberately going to make some things (well, one at least) that might seem like the right thing to do, but that will be changed later in the process. I've noted this one step as mistake in the text. I've also been quite verbose. If you just want the final hierarchy and components, scroll to the bottom and read Final Result.

    We want
    1. A scrollable vertical list of dynamically added items.
    2. The items should adjust their size to match their content.

    Creating the list item prefab
    Let's start by creating a basic prefab for the items that fulfills requirement 2. We'll start by creating the basic layout of it.
    • Create the following hierarchy:
      ListItem (Empty)
      - Date (Text)
      - Title (Text)
      - Image (Image)
      - Body (Text)

    Now we'll fix so that the Body text size adapts to its content.
    • To get our dynamic text object, i.e. Body, to adjust its size we add a Content Size Fitter. A Content Size Fitter is a Layout Controller that sets its own size. MISTAKE! (We will remove this component later!)
    • We want the text object to expand vertically so we set the Vertical Fit property of the Content Size Fitter to Preferred Size. This will tell the Content Size Fitter to set the height of our object to its preferred size. The value of the preferred size is set by the Text component.
    • The object's expansion direction is affected by the pivot. So to get the dynamic text to expand towards the bottom we move its pivot to the top by setting Pivot Y to 1 in the Rect Transform.
    Now our text object adjusts its size according to the content.
    • Create a prefab of our ListItem.
    Scroll hierarchy
    Now with the list item created, let's make it scrollable as per requirement 1.
    • Create the Dialogue game object with components Canvas Renderer, and Image.
    • Add a Scroll Rect object as a child to our Dialogue and set its size to the area in which we want the scrollable content to be visible. To hide items outside the Scroll Rect, also add a Mask and Image to the Scroll Rect.
    • Add a content object as child to our Scroll Rect. An empty GameObject will do for now. I'll name and refer to it as Content.
    • Set the Content property of the Scroll Rect to refer to our Content object.
    So far the hierarchy should look like the one you had already tried.

    Adjusting size of Content
    Content will contain our list items and Scroll Rect will enable scrolling if Content can't fit inside the Scroll Rect's area. So we need to make sure that Content's size adapts to the list items inside it. We also need to place the list items at correct positions within Content.
    • Add a Vertical Layout Group component to Content. This will place and size its children, i.e. our list items, vertically one after the other. It doesn't alter the size of Content though. So if the children don't fit in Content they might not get their preferred size. It can also lead to the scroll area behaving strangely. In my experience scrolling doesn't always seem to work as expected when the children of Content don't fit inside Content, but your mileage may vary. Let's fix the size of Content next.
    • Add a Content Size Fitter to Content. Set its Vertical Fit property to Preferred Size. The preferred size is set by the Vertical Layout Group which gets it by adding the preferred size of its children plus any spacing we've requested between the children (done via the Vertical Layout Group's Spacing property in the Inspector).
    Fixing list item position/preferred size
    Now if you only have one list item it might seem like things work. Add another list item to Content and you'll probably see it placed at the exact same position as the previous one! Wasn't the reason we added a Vertical Layout Group to Content to handle this? Yes it was, but its children, the list items, report a preferred size of 0. You can verify this in the Inspector if you select one of the list items. At the bottom is a Layout Properties window showing the preferred size of the list item.

    The reason is that by default a layout element has the value 0 for preferred size. (A layout element is what defines the properties minimum, preferred, and flexible for width and height. Any Game Object with a Rect Transform can function as a layout element. More specifically any component that implements the interface ILayoutElement.)

    So we need the list item to report a better preferred height. We can't set it to a fixed value, since the Body text height differs depending on its content. We want the list item to base its preferred height on that of its children's.
    I don't know of any suitable component shipped with Unity that does this though. The most apt one I can think of is Vertical Layout Group, which retrieves its preferred height from its children's, just like we want, BUT it also positions and sizes the children, which we do NOT want. So either we accept this possible repositioning and sizing of the children or we'll have to write our own layout element implementation. (Or have someone more versed in Unity tell us how it should be done properly ;) )

    If we take the former approach and add a Vertical Layout Group to list item, then the Body object's Content Size Fitter will add a warning, saying that you shouldn't have a Content Size Fitter if you have a layout group. This makes sense, since both of them try to adjust the object size. Since the preferred size of the Body object is set by the Text component and the Vertical Layout Group and Content components on the list item will use this preferred size to set the Body size, we don't need the Content Size fitter on the Text component anymore. Remove it.

    Alternatively, you can instead of using the Vertical Layout Group choose to implement your own layout element. Create a component that implements ILayoutElement that asks its children for their preferred size and reports their total as its preferred size. Then add this component to your list item. I haven't tried this.
    The choice is yours.

    Note that if you have some child of list item that is not an Image or Text, they might not report a preferred size other than 0. I'm not sure if all components calculate it.

    Fixing text expansion direction
    When you've got the Content height adapted to fit its children, you will probably see it expanding from the center. To get it to expand towards the bottom of the screen, set its pivot point Y coordinate to 1 in its Rect Transform.

    Hopefully, you'll now have a scroll list that works the way you want. Good luck!

    Final Result
    All objects except for Content and List item have a Canvas Renderer component.

    Dialogue (Image)
    -ScrollRect (Scroll Rect, Mask, Image)
    Anchors: At corners, placed where you want the visible scroll area.
    --Content (Vertical Layout Group, Content Size Fitter)
    Anchors+Corners: Stretch
    Pivot at top (Y=1)
    ---List item (Vertical Layout Group OR Custom layout element)
    ----Date (Text)
    ----Title (Text)
    ----Image (Image)
    ----Body (Text)
    Pivot at top (Y=1)


    I'm not sure if the anchors and corners for the list items have much effect, since they are changed by the auto layout anyway (Vertical Layout Group).
     
    Last edited: Apr 11, 2015
  7. andrew_fundscomein

    andrew_fundscomein

    Joined:
    May 3, 2015
    Posts:
    2
    Thank you for the very structured and clear answer! ContentSizeFitter was the component I was missing!
     
  8. AlanMattano

    AlanMattano

    Joined:
    Aug 22, 2013
    Posts:
    1,501
    I think There is need more tutorials about how to use all the Layout component section