Encapsulate child RectTransforms?

Math-Experts, hear me! (Brain-Teaser ahead!)

I’m basically trying to build a working content size fitter. A component, that resizes its own rect transform according to the encapsulated sum of all children rect transforms. And yes, I guess the Unity Content Size Fitter should do this, but it’s just does nothing.

Well, I’d say I’m pretty close, and already thought it would be working, but sometimes it is behaving strange and I don’t know why.
Here is what I want (grey are children, red their pivot points, green the desired result):


The Green box is what I want to get, just encapsulate all child transform, no matter their internal scale or pivot. just the “real” bounds.

This is my code so far:

    [ExecuteInEditMode]
    [RequireComponent(typeof(RectTransform))]
    public class ContentSizeMimic : UIBehaviour, ILayoutSelfController
    {
        private RectTransform[] _childTransforms;

        private RectTransform _myRect;
        private RectTransform RectTransform => _myRect == null ? GetComponent<RectTransform>() : _myRect;

        protected override void Awake()
        {
            base.Awake();
            _childTransforms = GetComponentsInChildren<RectTransform>().Where(rect => rect.gameObject != this.gameObject).ToArray();
        }
     
        // ToDo: potential performance issue
        private void Update() => CalculateParentRect();
     
        private void CalculateParentRect()
        {
            // If any child got deleted, fetch children again
            if (_childTransforms.Any(c => c == null))
                _childTransforms = GetComponentsInChildren<RectTransform>().Where(rect => rect.gameObject != this.gameObject).ToArray();
         
            // Using Vector4 to store: minX, maxX, minY, maxY
            Vector4 xXyY = new Vector4(float.MaxValue, float.MinValue, float.MaxValue, float.MinValue);
            foreach (var childRect in _childTransforms)
            {
                // Check for minimum X
                if (childRect.rect.xMin < xXyY.x)
                    xXyY.x = childRect.rect.xMin;
             
                // Check for maximum X
                if (childRect.rect.xMax > xXyY.y)
                    xXyY.y = childRect.rect.xMax;
             
                // Check for minimum Y
                if (childRect.rect.yMin < xXyY.z)
                    xXyY.z = childRect.rect.yMin;
             
                // Check for maximum Y
                if (childRect.rect.yMax > xXyY.w)
                    xXyY.w = childRect.rect.yMax;
            }
         
            Vector2 encapsulatingSize = new Vector2(Mathf.Abs(xXyY.y - xXyY.x), Mathf.Abs(xXyY.w - xXyY.z));
            if (RectTransform.sizeDelta.x != encapsulatingSize.x || RectTransform.sizeDelta.y != encapsulatingSize.y)
            {
                Log.Unicorn("This is called too often");
                RectTransform.sizeDelta = encapsulatingSize;
               LayoutRebuilder.MarkLayoutForRebuild(RectTransform);
            }
        }

        // From ILayoutSelfController Interface
        public void SetLayoutHorizontal() { }
        public void SetLayoutVertical() { }
    }

What am I doing wrong? Obviously RectTransform.rect.xMax is not giving me the furthest point to the right on the world x-axis.

1 Like

unity’s Content Size fitter only really works if the children have Layout Element components I think. Have you tried adding that? (Sorry this isn’t an answer to your actual question)

If that would work, this would solve my problem as-well. But I tried almost everything to make it do something. Just tried adding Layout Element components to the children, but it still doesn’t resize.
Also I’m getting a warning that I shouldn’t do that, because the parent element is a layout group (vertical) but this is all the reason I need to resize it in the first place ._.

Anyone? We are still facing this issue on a regular basis, and have very ugly dirty workarounds to get our layout groups to behave as desired.