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;
}