LayoutUtil: Automatically sizes a ScrollingFrame's UIGridLayout/UIListLayout

well in the second paragraph if i understand it correctly cant we do the same thing to every gui instance like put the module on replicated first require it and it will handle everything every gui instance make everything responsive so it scales well into mobile and pc’s without anything else just one line

1 Like

Would’ve saved me a lot of time organizing UIGridLayout’s and such, thanks for the amazing release!

1 Like

I’m not exactly sure what you’re asking me. But as described in the gifs provided, you could see the effect of using my module and not using it. Now in the comparison gif I showed a game that used offset for the CellSize. Rather if they used Scale it would end up stretching each element and making it look ten times worse. Fortunately my library solves both of those problems, but when you first create the ScrollingFrame and UIGridLayout it’s required to be done in scale. Also unless if it’s happening on immediate start of opening the game, you should be storing this in a place other then ReplicatedFirst (preferably somewhere only accessible by the client).

I am aware that you can’t change the size of an element inside of a ScrollingFrame while LayoutUtil is active, this is a feature that I’m hoping to include in the next update.

1 Like

I found a bug. If there is a UIPadding within the scrolling frame and there are a lot of children, it creates huge space at the top

A lot of children:
image

Fewer children:
image

EDIT: It also makes it so you can’t see the bottom row properly
image

1 Like

After testing it myself, UIPadding inside a ScrollingFrame has weird behavior. It seems to me like you meant to parent the UIPadding to the parent of the ScrollingFrame.

1 Like

LayoutUtil v1.0.0

The largest update yet following a complete rewrite of the codebase and documentation. Not only that, but there is now complete typescript support for use with roblox-ts. Recently, I was looking back at the code lately, wondering what is this for? Why did I add this? I included a ton of redundant features that were totally confusing or completely useless. This is a big reason for a complete remake.

For the contents of this update, I’ve fully integrated Luau type checking. Although this feature is still in beta (you might notice a lot of orange highlighted source code), if used properly it should make LayoutUtil a breeze. When linking a new UILayout, to ensure proper type checking, follow the short guide below.

local class: LayoutUtil.List = LayoutUtil.new(script.Parent.ScrollingFrame) -- considering theres a UIListLayout inside

It’s as simple as adding a colon followed by LayoutUtil.List for a UIListLayout, or LayoutUtil.Grid for a UIGridLayout.

That’s it? Nahhhh, I’ve implemented a variety of methods including:

  • .new(layout: UIListLayout | UIGridLayout, noStartup: boolean) - The noStartup parameter is new, replacing the configuration table. This parameter determines if it will immediately start resizing everything (otherwise call :Play() to start).

  • :UpdatePadding() - Updates the padding manually if it’s paused

  • :UpdateSize() - Updates the children size or CellSize manually if it’s paused.

  • :UpdateCanvas() - Updates the canvas manually if it’s paused.

  • :Update() - Calls the three functions above.

  • :Play() - Plays it so that it automatically updates.

  • :Pause() - Pauses it.

  • :Destroy() - Destroys the class

And a couple of read-only properties:

  • .Ref: UIGridLayout | UIListLayout - A reference to the UILayout.

  • .Paused: boolean - Whether or not it’s paused.

  • .Destroyed: boolean - Whether or not it’s destroyed.

And a couple of interchangeable properties:

  • DoUpdatePadding: boolean - Whether or not to update the padding.

  • DoUpdateSize: boolean - Whether or not to update the children’s size or CellSize.

  • DoUpdateCanvas: boolean - Whether or not to update the CanvasSize of the ScrollingFrame.

Unfortunately, you will have to alter your code to work with the latest API if you have previously used the “advanced” functionality. For the better, this was done. If you have imported LayoutUtil directly from the Roblox catalog, there is no need to worry, for this version I have created a new asset (which I suggest to integrate).

Before version 1.0.0 you had to use methods like :SetDefault(), which was actually the only method that only allowed you to change the default padding (and CellSize for UIGridLayouts). In the latest version you are able to directly change the properties of a UIGridLayout/UIListLayout including Padding, CellSize, CellPadding, and the size of any child within a UIListLayout. LayoutUtil automatically adapts to those changes and retains the aspect ratio around that size.

There were a few bugs located and squashed, unfortunately, I forgot to record all of them. There was a strange behavior that prevented it from working while adjusting the FillDirection during runtime.

I intended on adding a feature to automatically ignore the CanvasSize on startup, but after careful consideration, there would be too many minor exceptions. For example, let’s say you have a template that you clone into the ScrollingFrame and it’s supposed to be relative to the size of the canvas from startup, the slot would end up compressing. I understand that having to resize the canvas after you had everything perfectly sized is frustrating, but that’s where my new plugin comes to play.

Introducing LayoutUtilPlugin

The plugin to end the clash between scale and offset. It contains a total of five buttons:

  • Scale - Converts the selected GuiObject to scale.

  • Offset - Converts the selected GuiObject to offset.

  • RatioConstraint - Inserts a UIAspectRatioConstraint into the selected objects, automatically calculating the AspectRatio.

  • Remove Canvas - Sets the CanvasSize to all zeros while maintaining the size of each child, which makes LayoutUtil happy.

  • Import - Imports the latest version of LayoutUtil into the StarterGui.

A few key features include:

  • The ability to convert UIGridLayouts, UIListLayouts, UIPaddings, UICorners, UIPageLayouts, and UITableLayouts to scale or offset.

  • Selecting a ScreenGui (or BillboardGui, SurfaceGui, &, etc) will convert every descendant.

  • Converting elements in a ScrollingFrame will make sure it’s size stays consistent.

  • Automatically converts the CanvasSize to scale (which shouldn’t be too important for LayoutUtil).

  • Utilizes ChangeHistoryService so you could easily revert changes.

  • Removing the CanvasSize from a ScrollingFrame will keep the size of each child (accounts for UIGridLayouts).

  • A lot of edge cases are checked, being if the parent is a type of Gui it will use the screen size, otherwise if it’s not a GuiObject it won’t resize. If the property is being affected by the X or Y axis (ex: FillDirection & more in-depth ones).

Hopefully, this plugin should make working with UI in general seamless.

If there are any questions, feature requests, or bugs, please be sure to let me know. The best way to get in contact is via Discord, but I actively read DevForum DMs.

Roblox - iiNemo
Discord - nickk#9163

Documentation | Repository | Releases | Roblox Library | Plugin

7 Likes

Thanks for this fantastic contribution, will be very useful!

1 Like

Awesome plugin. Is there a way to automatically add a little bit of canvas size on the bottom in case some of your contents get cut off?
https://gyazo.com/f5bd03f96789ccae0b8f11397cc53fce
As you can see in this GIF, it cuts off the scrolling frame right when the contents end which can look a little bit awkward in some cases, especially when there is padding on top.

1 Like

Interesting situation, but unfortunately there is currently no way to add CanvasSize padding. You could fix this problem by having a container frame with a ScrollingFrame inside, but I could see how that is inconvenient and doesn’t have the exact same behavior. I’m actually planning on revamping the library and approaching it from a whole new angle which should solve issues such as this one.

3 Likes

Am I doing something wrong here?
https://i.gyazo.com/c2dcdb4700aba78bfe4fa5dc2f81ee25.mp4
Left side is the intended size of each layout item (scroll bar omitted), right side is with LayoutUtil.

The problem seemed to fix itself when I moved the UI code to a LocalScript (only had it in a server script because I was using a surface GUI).

However, I’ve stumbled upon another problem albeit more minor. The scroll bar inset doesn’t seem to work when it appears in the scrolling frame:

https://i.gyazo.com/295b37697851de7ec4d317ffffa3868d.mp4

You can see the word ‘wipeouts’ gets cut off in the last second of the gif.

ScrollingFrame properties:

image

1 Like

It seems that ScrollBarInset doesn’t work with Offset and LayoutUtil doesn’t automatically account for that. I’ve been busy lately, but I’m still planning on revamping this library which will definitely fix bugs like this.

1 Like

Something that would be useful for the plugin is if it was just one single button in the toolbar that popped out as another window, as to not clog up the toolbar.

1 Like

I’m actually planning on releasing the next version within a few days. This is something I’ve taken into account, as I don’t like my buttons being cluttered as well.

1 Like

LayoutUtil v2.0.0 icon-32

Actions Status Latest Release

In version 2.0.0, LayoutUtil has been completely rewritten… yet again. This time, it’s much much better >:). No more of the useless and bloated API. LayoutUtil has been completely cleaned up and is composed of only a single function that can be simply called to apply changes. Along with this, I highly suggest ONLY using the plugin, where no scripting at all is involved (as described below). Circumstance will determine whether you need the library during run-time, but it’s always something to keep in mind. The new version of LayoutUtil, no longer allows you to specify the ScrollingFrame directly. This was done to be more explicit and to make the code more readable. With that, this feature was removed due to it being unnecessary and only contributing to the bloat.

To get into the specifics, LayoutUtil no longer has any connections, any run-time manipulation, it simply utilizes and pre-calculates UIAspectRatioConstraints. With the new release of the AutomaticCanvasSize property of a ScrollingFrame, you simply don’t need to worry about any prerequisites. This means, no more having to rely on converting your UI to scale (although I strongly suggest doing so). Setting your CanvasSize to (0, 0, 0, 0) is optional, but while using the AutomaticCanvasSize property, it will act as canvas size padding. Unfortunately with this property being so new, there are specific problems as described in this thread, but they should be quickly resolved over time.

One thing to note is LayoutUtil doesn’t assign the AutomaticCanvasSize property, this should be done by the user. As a side note, some people will say “then what’s the point of LayoutUtil?” Its primary goal was to maintain the aspect ratio of each child element within a ScrollingFrame, while utilizing these layouts. This is what it currently does and what it will be doing over updates to come.

Typescript Users

Typescript is still supported under roblox-ts and version 2.0.0 has been published to npm.

Roact Users

Unfortunately with how Roact works, a LayoutUtil component just simply isn’t feasible. I’ve tried a variety of solutions, all leading to dead-ends. Although you could fork the code and create a component to automatically calculate a UIAspectRatioConstraint’s aspect ratio. A friend recommended this open-sourced component, created by @sayhisam1, which utilizes UIScales uniquely and successfully. You can read more about it within the code, but it seems to really help overall with designing UI, especially for keeping a consistent size across all resolutions.

LayoutUtil-Plugin v2.0.0

Yes, even the plugin had a complete remake. LayoutUtilPlugin gets straight to the point, none of the extra fancy buttons to help with designing your UI, you could simply press the single button while selecting your UILayout/ScrollingFrame(s) to automatically insert and calculate the UIAspectRatioConstraints. LayoutUtilPlugin uses the LayoutUtil library internally… obviously, but I highly recommend it if possible as it would save unnecessary coding.

LayoutUtil-Plugin v1.0.0 did have a few handy extra UI tools, I plan to release a separate plugin which is specifically for this. I know most converter plugins aren’t as in-depth with converting and aren’t as supportive, which is why this could be useful. Not only that, but automatically calculating aspect ratios is a gift from heaven. There is no guarantee when this will be released, which is why I say it will be some time in the future - stay tuned.

Where are the docs?

Documentation isn’t needed, there is not much of an API and it’s fairly basic - straight to the point. The README.md suffices and should be enough to get a complete understanding of how to use it. Although if there are any questions or concerns, feel free to DM me.

Examples

Obviously, in this example, you’d need to define LayoutUtil and myUIGridLayout.

LayoutUtil(myUIGridLayout)

LayoutUtil also provides a second parameter if the parent of your UILayout hasn’t been set. This should normally be defined as your ScrollingFrame, although it also accepts a Vector2 since it just needs the AbsoluteSize. If a non-GuiObject is passed it will assume the parent size is the screens resolution. If a GuiObject is passed, it will use its AbsoluteSize.

LayoutUtil(myUIGridLayout, ScrollingFrame)

Contact

Roblox: iiNemo
Discord: nickk#9163 - Try to DM me directly rather than adding (for a quicker response).

Repository | Release | Roblox Catalog | Plugin

9 Likes

This is amazing, I’ve been struggling with this issue for a very long time.

1 Like

how do i use this???

I have been trying to use this for the past 30 minutes and still have no idea why it won’t work.

The grid layout is written in scale and the canvas size is {0,0} {0,0}

This is a local script I put inside of the grid layout:

local LayoutUtil = require(game:GetService('ReplicatedStorage').moduleScripts.LayoutUtil) 
LayoutUtil(script.Parent) 

I also tried the “LayoutUtil.new(script.Parent)” that you told someone else to do, but that didn’t work either.

Any help is appreciated!

3 Likes

It doesn’t let me scroll… When I use the AutomaticCanvasSize it lets me scroll, but moves all the frames in the scrolling frame down and cutting half of the last frame off (image below for reference).

(No it doesn’t let me scroll any further)
image

1 Like

Hey I’m a little confused about how to use the LayoutUtil v2 module.

Using the old LayoutUtil module like this:

require(script:WaitForChild("LayoutUtil")).new(ShopScrollingFrame:WaitForChild("UIGridLayout"))

gets me the result that I’ve been wanting, here:

I’ll probably end up just sticking with this. However if there’s some performance improvement or other optimization with the new one, I’d prefer to use that one. The problem is that it doesn’t do anything though.

Code: require(script:WaitForChild("LayoutUtil2"))(ShopScrollingFrame:WaitForChild("UIGridLayout"))

Result:

You must set the AutomaticCanvasSize property of the ScrollingFrame.