Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Dynamically sized scroll-able list. Need some help here!

Discussion in 'UGUI & TextMesh Pro' started by Fingar, Oct 28, 2015.

  1. Fingar

    Fingar

    Joined:
    Feb 21, 2013
    Posts:
    10
    Alright, guys! I'll try to explain to my best ability.

    What I am trying to do:
    I am trying to make a horizontal scroll-able list of images that all have the same size (1024x1024px). This list contains 1 to x images based on the state of the game.

    All images should be correctly scaled to fit a %-value of the vertical screen size while still keeping their aspect ratio so that the image-list look the same on most/all screen sizes. The images should be aligned to the left side of the list (so if you only have one image in the list, it doesn't appear in the middle of the list, but at the left side).

    The list itself (we're talking the scroll-able area) is re-sized to the content of the list (this is so that the player can scroll between images when the list is too long for the screen size, but that you can't scroll "away" from from the images if you don't have enough images to go outside the screen).

    - Does this make sense? Should be easy, right?

    How I have done it this far:

    Step 1:

    Created a panel that takes up the desired screen-space and set the anchors to the same position as the re-size-handles like shown here in Figure 1. It will contain the Scroll Rect-component and control its child panel.

    Figure 1: This is the panel that will contain the other elements as well as the canvas.

    Figure 1.png

    Step 2:
    Created a new child panel that stretches vertically to fill its parent with anchors and pivot point aligned at the left side like shown in here in Figure 2. This panel will be the parent of all the images in the list.

    Figure 2: Here is the child panel.
    Figure 2.png

    To make sure that the children-images is placed correctly in line, this panel has a Horizontal Layout Group-component. Child Alignment is set to "Middle Center" and Child Force Expand is set to only work on the height of the images (check Figure 3).

    I also added a Content Size Fitter-component on this panel with a Horizontal Fit to "Preferred Size" so that the panel it self expands horizontally, effectively giving me a bigger scrolling area as the list grows (check Figure 3).

    Figure 3: The values of my Horizontal Layout Group and Content Size Fitter.
    Figure 3.png

    Step 3:
    Created x number of child images that are set to Image Type "Simple" and Preserve Aspect to "true".


    The problem:
    The images are not actually placed by the Horizontal Layout Group, but scaled. Even though the Image-component displays the image with the correct aspect ratio, the Rect Transform-component (in this case; controlled by the Horizontal Layout Group) is still stretched pretty bad (effect is of course worse the smaller the screen is because.... pixels...).

    I've tried to give the images a Layout Element-component, playing around with the Preferred Width to override this horizontal stretching. But because these values are pixel-based, I can make it look good on one screen size, on others I end up with pretty much the same problem.

    I tried to give the images a Aspect Ratio Fitter-component, trying get the height to control the width of the image Rect Transforms. But because the Rect Transform is already controlled by the Horizontal Layout Group, that is not allowed.

    I figured the Layout Element-component is what lets me make the exceptions in the parents layout controls. So I tried to extend the Layout Element-component with some tools that lets me define the values with percentage instead. But I'm no programmer.. and the backend of the UI system is a little hard to get around, at least with all these layout group dependencies and Rect Transforms.

    Figure 4: Here is how much wider than the image the Rect Transform has streatched.
    Figure 4.png

    Figure 5: When all my testing images are put in, this is how long the whole scroll-area becomes.
    Figure 5.png


    Have I done something wrong here? Can't figure it out...
    Just post if you need more info.

     
    Last edited: Oct 28, 2015
  2. Fingar

    Fingar

    Joined:
    Feb 21, 2013
    Posts:
    10
    Here are some more images:

    Figure 6: This is pretty much what I'm looking for. I used Layout Element on all images and set Preferred Width to something that looked nice.
    Figure 6.png

    Figure 7: After reducing my screen size, its back to this.
    Figure 7.png

    Figure 8: Here I'm using the same technique from a small screen size.
    Figure 8.png

    Figure 9: When I change to a larger screen size, this is what I end up with. Not what I'm looking for...
    Figure 9.png
     
  3. Fingar

    Fingar

    Joined:
    Feb 21, 2013
    Posts:
    10
    I put up a skeleton of the project that shows the problem in a .zip-file so you can take a closer look at the problem. Check the link bellow.

    The build-platform is set to PC, Mac & Linux so the re-sizing will only be based on aspect ratio. You can still see the problem when scaling up and down, but for a better perspective: try to switch the build-platform to OSX or Android and try out one of the resolution-based sizes.

    NEW LINK: https://drive.google.com/file/d/0Bx_uJDu3XtfsTDZlT19UbFZMYTA/view?usp=sharing
     
    Last edited: Nov 4, 2015
  4. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,685
    Hi
    Tried to download your sample, however the link no longer works :S.

    Looking at your issue, here's a few tips from what I can see.
    • To enable scaling up/down of the parent container, make sure you set it's anchors to FILL, that way it will size with the canvas. (if you hold down the ALT key when changing the Anchors, it will also resize the RectTransform to match, if you don't want that, dont hold ALT)
    • If you don't want it to scale down below a certain size (like with screenshot 3), use Min width rather than preferred. then it will scale up but not scale below a certain value
    • From the looks of it, you need to play with he anchors of the images or child components
    As an alternative, try the new ScollSnap control in the UI Extensions project (the Fixed Item scroll with multiple child option in the Editor menu, GameObject -> UI -> Extensions -> Fixed Item scroll). or the Horizontal Scroll Snap (two similar controls with different patterns.

    Let me know how you get on.
     
  5. Fingar

    Fingar

    Joined:
    Feb 21, 2013
    Posts:
    10
    Hey! Thanks for quick reply!

    I've been working on my own horizontal layout group that forces the scaling and positioning for the children and parent size fitting to work like I want it to. But it's really clunky at this point. The original solution is much more elegant if I just got it to work... :I

    I looked over your bullet points!
    • The anchor points of the parent should be correctly positioned. I have plans for other UI-content over and under the panel want the layout on. Changing the anchors to fill-mode would just fill the whole parent, in this case making no room for those other UI-elements.
    • But changing the Min-width... then I'm still messing around with pixel sizes, right? Won't I end up with same problems on smaller screen sizes ?
    • About the anchors of the images, they are controlled by the Horizontal Layout Group, can't change them :/
    I looked at the horizontal snap scroll, but it doesn't fix my problem. Sorry :/
    Didn't find the Fixed Item Scroll. But that was pretty much the same, right?

    I uploaded the project to my google drive instead so the link doesn't go nuclear again, so if you got the time for it please take a look.

    NEW LINK: https://drive.google.com/file/d/0Bx_uJDu3XtfsTDZlT19UbFZMYTA/view?usp=sharing

    If you're still wondering what I'm rambling about, I could upload a screencast-video explaining what I'm trying to do!
    Thanks again!
     
  6. SimonDarksideJ

    SimonDarksideJ

    Joined:
    Jul 3, 2012
    Posts:
    1,685
    A screencast would help so we are not talking at cross purposes
     
  7. Fingar

    Fingar

    Joined:
    Feb 21, 2013
    Posts:
    10
    Made screencast! Hope this helps!

     
  8. DWilliams

    DWilliams

    Joined:
    Jan 12, 2015
    Posts:
    63
    I think you were on the right track with layout elements on each child. It's just that you'll have to have a script modify the preferred width based on whatever the height gets set to by the scaling of the parent scroll area.

    So each child could have something like myLayoutElement.preferredWidth = myRectTransform.rect.height;
    and you could do that each update if needed but if your resolution isn't changing you can just do it once.
     
  9. Fingar

    Fingar

    Joined:
    Feb 21, 2013
    Posts:
    10
    Thanks for quick answer!
    Any way to see the source code of the Layout Element-component?
    I don't want to override something that may ruin the rest of the script. Maybe I can just do it right after the "base.something();" without taking up to much resources?
     
  10. Fingar

    Fingar

    Joined:
    Feb 21, 2013
    Posts:
    10
    Wellwellwellwell....
    3 lines of code did the magic!

    It's not dynamic for different usage now, but if more people are messing around with this...
    Here you go:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3. using System;
    4.  
    5. public class MyLayoutElement : LayoutElement
    6. {
    7.     private RectTransform RectTrans;
    8.  
    9.     protected override void Start()
    10.     {
    11.         DoSomething(base.Start);
    12.     }
    13.  
    14.     protected override void OnValidate()
    15.     {
    16.         DoSomething(base.OnValidate);
    17.     }
    18.  
    19.     private void DoSomething(Action callback)
    20.     {
    21.         if (callback != null) callback();
    22.         RectTrans = transform.parent.GetComponent<RectTransform>();
    23.         preferredWidth = RectTrans.rect.height;
    24.     }
    25. }
    Thanks to @DWilliams for pointing me in the right direction!
     
    Last edited: Nov 6, 2015