I absolutely need to use scale for the contents of my ScrollingFrame and I need the CanvasSize to automatically adjust depending on how many items there are. I am using a UIGridLayout to scale my items.
Everything I’ve seen that does this uses offset to accomplish this. I need to use scale. I have no clue where to begin!
If it helps, the size of my items are {0.21, 0},{0.1, 0}. Does anyone have a clue what to do?
I’ve got your problem too. I was gonna complain about it yesterday with a feature request or something, but I decided against it for whatever reason.
At this stage I’m using scale for the size of the items in the ScrollingFrame, and because of how my game works with selling items, only hoarders will probably fill it up to a size where it needs to scroll. However, I haven’t tested mobile users yet.
LUCKILY we do have one solution. It kinda sucks but it’s also a fine solution. What is it, you ask? Well we just manually resize it using offset every time the AbsoluteSize of the ScrollingFrame changes. It’s easy enough:
-- Desired values in scale
local PADDING = Vector2.new(0.02, 0.02)
local SIZE = Vector2.new(0.2, 0.2)
ScrollingFrame:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
local AbsoluteSize = ScrollingFrame.AbsoluteSize
-- Convert desired values to offset
local NewPadding = PADDING * AbsoluteSize
NewPadding = UDim2.new(0, NewPadding .X, 0, NewPadding .Y)
local NewSize = SIZE * AbsoluteSize
NewSize = UDim2.new(0, NewSize.X, 0, NewSize.Y)
UIGridLayout.CellPadding = NewPadding
UIGridLayout.CellSize = NewSize
end)
I’ll probably implement this into my own game when I feel like it. Now that I think about it, it’s surprisingly easy, so I have no reason to procrastinate hah.
The problem here is that AbsoluteContentSize is what the Scale property of objects inside the ScrollingFrame use. As AbsoluteContentSize increases, so does the size of the grid items and the padding (if they were sized with Scale). To fix this, the AbsoluteSize property is what should be used in this case as it is completely unrelated to the scrolling canvas (and therefore relatively static).
I normally set the CanvasSize to UDim2.new(0, 0, 0, 0) then I design a template of the contents in the ScrollingFrame and set up the UIGridLayout then once I am happy with it I insert the UIAspectRatioConstraint parented to the UIGridLayout. Then you change the CanvasSize based on the AbsoluteContentSize.Y of the UIGridLayout.
I hate to bump this thread but I ran into this problem again. I realized your solution doesn’t actually adjust the CanvasSize of the ScrollingFrame. How can I adjust that so that the scrollbar works properly?
I appreciate you looking at this again. That didn’t work though. The scrollbar isn’t the proper length. I had to modify your code as well to get rid of an error, so maybe I messed something up? This is what it looks like right now:
Oh, sorry about those errors I was quite rushed when I was writing it at the time
That could should work fine AFAIK, when I have used it in the past the canvas size fit perfectly. How off is the scrollbar length? Is it just a little bit or is it massive?
Just looking at your code, I can only see one issue - the line setting the cell padding (UIGridLayout.CellPadding = NewPadding) is missing. That’s probably the cause if the scrollbar length isn’t off by much.
I’ve simplified your code almost completely, does this help? Try using this exact code.
local PADDING = Vector2.new(10, 10) -- Offset
local SIZE = Vector2.new(0.2, 0.2) -- Scale
-- This only needs to be set once since the padding is not in Scale
UIGridLayout.CellPadding = UDim2.new(0, PADDING.X, 0, PADDING.Y)
ScrollingFrame:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
local NewSize = SIZE * ScrollingFrame.AbsoluteSize
UIGridLayout.CellSize = UDim2.new(0, NewSize.X, 0, NewSize.Y)
ScrollingFrame.CanvasSize = UDim2.new(0, UIGridLayout.AbsoluteContentSize.X, 0, UIGridLayout.AbsoluteContentSize.Y)
end)
You can also remove the code to do with padding completely and set it all in the editor if you want to make this as simple as possible (which is a good idea if you’re having issues).
The only issue with my code I can see is that it does not account for padding when calculating the scale of elements. To do this we’d need to do something like:
-- Add one because with two elements there'd be three padding spaces, and so on
local NumColumns = (1 / SIZE.X) + 1
-- We only take away from the X axis because the Y axis is what makes the scrolling frame expand
-- However if you want to keep aspect ratio, do NewSize.Y - UIGridLayout.CellPadding.Y.Offset
NewSize = Vector2.new(NewSize.X - (UIGridLayout.CellPadding.X.Offset * NumColumns), NewSize.Y)
local scrollingFrame = script.Parent
local uiGridLayout = scrollingFrame.UIGridLayout
local SIZE = Vector2.new(uiGridLayout.CellSize.X.Scale, uiGridLayout.CellSize.Y.Scale)
local PADDING = Vector2.new(uiGridLayout.CellPadding.X.Scale, uiGridLayout.CellPadding.Y.Scale)
local function ResetScroll()
local NewSize = SIZE * scrollingFrame.AbsoluteSize
local NewPadding = PADDING * scrollingFrame.AbsoluteSize
uiGridLayout.CellSize = UDim2.new(0, NewSize.X, 0, NewSize.Y)
uiGridLayout.CellPadding = UDim2.new(0, NewPadding.X, 0, NewPadding.Y)
scrollingFrame.CanvasSize = UDim2.new(0, uiGridLayout.AbsoluteContentSize.X, 0, uiGridLayout.AbsoluteContentSize.Y)
end
ResetScroll()
uiGridLayout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(ResetScroll)
I am sorry to revive this thread but I recently needed to use a similar thing in my game and I stumbled upon your answer. The idea to use AbsoluteSize property is apt but the property doesn’t change on dynamic addition/ removal of elements. So instead I connected to the property “AbsoluteContentSize” of the UIGridLayout and made some other minor modifications. Now you can set the Cell padding and Size [scale wise] in the editor and the script would look after the rest.
Yep, however one issue there. Your code is cyclically calling ResetScroll – when the AbsoluteContentSize changes you call ResetScroll to modify the CellSize, which then changes the AbsoluteContentSize and then you call ResetScroll… and so on.
In your case this might not be much of an issue as its probably only looping maybe twice or thrice due to your calculations being deterministic, but it is prone to error (and is doing redundant work) which is why I think it’s good practice to avoid that.
Another thing to note is that now resizing the screen (and/or the ScrollingFrame and its ancestors) will not call ResetScroll because the AbsoluteContentSize doesn’t change because the cells have their sizes in Offset, not in Scale. So the AbsoluteSize listener is still necessary if you want to be robust and handle this.
TLDR calling it like you are now will call it at least twice for every time an item or a row (depending on the number of items in the grid) is added or removed, and will not call it at all when the screen size changes.
What I think is better is binding ResetScroll to when AbsoluteSize changes, and also calling it manually after you add/remove elements from the grid:
ScrollingFrame:GetPropertyChangedSignal("AbsoluteSize"):Connect(ResetScroll)
-- when you're doing stuff with items
for i = 1, #ItemsToGenerate do
local Item = MakeNewItem()
Item.Parent = ScrollingFrame
end
ResetScroll()
This does less work, as now the ResetScroll function only gets called when a) the ScrollingFrame changes size (usually due to screen size changing, which is rare), or b) you’re done modifying the items in the grid and want to tidy it up.