Set a RectTranform's pivot without changing its position

Is there a way to change a RectTransform’s pivot point without changing its position? If not, does someone know the math I would have to do to simulate this behavior? Thanks in advance!

Well, I managed to figure this one out on my own. Implemented as an extension method of the RectTransform class. Currently only works properly for XY axis-aligned objects.

    public static void SetPivot(this RectTransform rectTransform, Vector2 pivot)
    {
        if (rectTransform == null) return;

        Vector2 size = rectTransform.rect.size;
        Vector2 deltaPivot = rectTransform.pivot - pivot;
        Vector3 deltaPosition = new Vector3(deltaPivot.x * size.x, deltaPivot.y * size.y);
        rectTransform.pivot = pivot;
        rectTransform.localPosition -= deltaPosition;
    }

Here is a cleaned up version of previous commenters’ extension function, which supports scaling & rotation.

/// <summary>
/// Set pivot without changing the position of the element
/// </summary>
public static void SetPivot(this RectTransform rectTransform, Vector2 pivot)
{
    Vector3 deltaPosition = rectTransform.pivot - pivot;    // get change in pivot
    deltaPosition.Scale(rectTransform.rect.size);           // apply sizing
    deltaPosition.Scale(rectTransform.localScale);          // apply scaling
    deltaPosition = rectTransform.rotation * deltaPosition; // apply rotation
    
    rectTransform.pivot = pivot;                            // change the pivot
    rectTransform.localPosition -= deltaPosition;           // reverse the position change
}

public static void SetPivot(RectTransform target, Vector2 pivot)
{
if (!target) return;

        var offset=pivot - target.pivot;
        offset.Scale(target.rect.size);
        var wordlPos= target.position + target.TransformVector(offset);

        target.pivot = pivot;
        target.position = wordlPos;
    }

To expand on this some, rotation and scale can be accounted for on a single line:

public static void SetPivot(this RectTransform rectTransform, Vector2 pivot)
{
	if (rectTransform == null) return;
	
	Vector2 size = rectTransform.rect.size;
	Vector2 deltaPivot = rectTransform.pivot - pivot;
	Vector3 deltaPosition = rectTransform.rotation * new Vector3((deltaPivot.x * size.x) * rt.localScale.x, (deltaPivot.y * size.y) * rt.localScale.y);
	rectTransform.pivot = pivot;
	rectTransform.localPosition -= deltaPosition;
}

Both the answers by @wck19870829 and @LiterallyJeff (with the rectTransform.rotation change by @jomalomal) work.

I made it a little easier to get the Vector2 position via an enum.

public static void SetPivot(this RectTransform target, PivotPresets preset)
        {
            target.SetPivot(GetVector2FromPivot(preset));
        }
        
        public static void SetPivot2(this RectTransform target, PivotPresets preset)
        {
            target.SetPivot2(GetVector2FromPivot(preset));
        }
        /// <summary>
        /// Point of this is to change the pivot, without moving the object. Method 1
        /// </summary>
        public static void SetPivot(this RectTransform target, Vector2 pivot)
        {
            if (target == null) return;
            var offset=pivot - target.pivot;
            offset.Scale(target.rect.size);
            var wordlPos= target.position + target.TransformVector(offset);
            target.pivot = pivot;
            target.position = wordlPos;
        }
        /// <summary>
        /// Point of this is to change the pivot, without moving the object. Method 2
        /// </summary>
        public static void SetPivot2(this RectTransform rect, Vector2 pivot)
        {
            if (rect == null) return;
            Vector3 deltaPosition = rect.pivot - pivot;    // get change in pivot
            deltaPosition.Scale(rect.rect.size);           // apply sizing
            deltaPosition.Scale(rect.localScale);          // apply scaling
            deltaPosition = rect.transform.localRotation * deltaPosition; // apply rotation
     
            rect.pivot = pivot;                            // change the pivot
            rect.localPosition -= deltaPosition;           // reverse the position change
        }

public enum PivotPresets
    {
        TopLeft,
        TopCenter,
        TopRight,
 
        MiddleLeft,
        MiddleCenter,
        MiddleRight,
 
        BottomLeft,
        BottomCenter,
        BottomRight,
    }

public static Vector2 GetVector2FromPivot(PivotPresets preset)
{
switch (preset)
{
case (PivotPresets.TopLeft):
{
return new Vector2(0, 1);
}
case (PivotPresets.TopCenter):
{
return new Vector2(0.5f, 1);
}
case (PivotPresets.TopRight):
{
return new Vector2(1, 1);
}
case (PivotPresets.MiddleLeft):
{
return new Vector2(0, 0.5f);
}
case (PivotPresets.MiddleCenter):
{
return new Vector2(0.5f, 0.5f);
}
case (PivotPresets.MiddleRight):
{
return new Vector2(1, 0.5f);
}
case (PivotPresets.BottomLeft):
{
return new Vector2(0, 0);
}
case (PivotPresets.BottomCenter):
{
return new Vector2(0.5f, 0);
}
case (PivotPresets.BottomRight):
{
return new Vector2(1, 0);
}
}
return default;
}

public static Vector2 GetVector2FromPivot(PivotPresets preset)
{
switch (preset)
{
case (PivotPresets.TopLeft):
{
return new Vector2(0, 1);
}
case (PivotPresets.TopCenter):
{
return new Vector2(0.5f, 1);
}
case (PivotPresets.TopRight):
{
return new Vector2(1, 1);
}
case (PivotPresets.MiddleLeft):
{
return new Vector2(0, 0.5f);
}
case (PivotPresets.MiddleCenter):
{
return new Vector2(0.5f, 0.5f);
}
case (PivotPresets.MiddleRight):
{
return new Vector2(1, 0.5f);
}
case (PivotPresets.BottomLeft):
{
return new Vector2(0, 0);
}
case (PivotPresets.BottomCenter):
{
return new Vector2(0.5f, 0);
}
case (PivotPresets.BottomRight):
{
return new Vector2(1, 0);
}
}
return default;
}