Mobile Safe Area Handler
Automatically adjusts UI RectTransform to respect device safe areas on notched phones and tablets. Supports runtime orientation changes and works across iOS and Android devices.
How to Use
Create a full-screen RectTransform panel as a child of your Canvas
Attach the MobileSafeArea script to that panel
Place all your UI elements inside this safe area panel
Toggle applyTop/Bottom/Left/Right to control which edges are inset
Enable simulateInEditor to preview notch behavior in the Unity editor
Features
- Automatic safe area detection
- RectTransform adjustment for notches
- Works on all devices (iOS/Android)
- Runtime orientation change support
- Editor simulation support
When to Use This
Required for any mobile game shipping to modern phones with notches, cutouts, or rounded corners (iPhone, Samsung Galaxy, Pixel). Attach this to your main UI container to prevent interactive elements from being hidden behind hardware obstructions.
Common Mistakes
This script must be on a RectTransform that stretches to fill the Canvas — if the anchors aren't set to (0,0)-(1,1) before applying, the safe area insets will be calculated incorrectly. The simulateInEditor option only simulates a top notch; real devices may have bottom bars or side cutouts too. Don't apply this to every UI panel — attach it to one root container and parent all UI inside it, or you'll get double-inset issues.
Source Code
using UnityEngine;
[RequireComponent(typeof(RectTransform))]
public class MobileSafeArea : MonoBehaviour
{
[Header("Settings")]
[SerializeField] private bool updateContinuously = true;
[SerializeField] private bool applyTop = true;
[SerializeField] private bool applyBottom = true;
[SerializeField] private bool applyLeft = true;
[SerializeField] private bool applyRight = true;
[Header("Editor Simulation")]
[SerializeField] private bool simulateInEditor;
[SerializeField] private float simulatedNotchHeight = 80f;
private RectTransform rectTransform;
private Rect lastSafeArea;
private Vector2Int lastScreenSize;
private ScreenOrientation lastOrientation;
private void Awake()
{
rectTransform = GetComponent<RectTransform>();
ApplySafeArea();
}
private void Update()
{
if (!updateContinuously) return;
if (HasScreenChanged())
{
ApplySafeArea();
}
}
private bool HasScreenChanged()
{
Rect safeArea = GetSafeArea();
Vector2Int screenSize = new Vector2Int(Screen.width, Screen.height);
ScreenOrientation orientation = Screen.orientation;
if (safeArea != lastSafeArea || screenSize != lastScreenSize || orientation != lastOrientation)
{
lastSafeArea = safeArea;
lastScreenSize = screenSize;
lastOrientation = orientation;
return true;
}
return false;
}
private void ApplySafeArea()
{
Rect safeArea = GetSafeArea();
if (Screen.width <= 0 || Screen.height <= 0) return;
Vector2 anchorMin = safeArea.position;
Vector2 anchorMax = safeArea.position + safeArea.size;
anchorMin.x /= Screen.width;
anchorMin.y /= Screen.height;
anchorMax.x /= Screen.width;
anchorMax.y /= Screen.height;
if (!applyLeft) anchorMin.x = 0f;
if (!applyBottom) anchorMin.y = 0f;
if (!applyRight) anchorMax.x = 1f;
if (!applyTop) anchorMax.y = 1f;
anchorMin.x = Mathf.Clamp01(anchorMin.x);
anchorMin.y = Mathf.Clamp01(anchorMin.y);
anchorMax.x = Mathf.Clamp01(anchorMax.x);
anchorMax.y = Mathf.Clamp01(anchorMax.y);
rectTransform.anchorMin = anchorMin;
rectTransform.anchorMax = anchorMax;
rectTransform.offsetMin = Vector2.zero;
rectTransform.offsetMax = Vector2.zero;
}
private Rect GetSafeArea()
{
#if UNITY_EDITOR
if (simulateInEditor)
{
return new Rect(
0f,
0f,
Screen.width,
Screen.height - simulatedNotchHeight);
}
#endif
return Screen.safeArea;
}
public void ForceUpdate()
{
ApplySafeArea();
}
public Rect GetCurrentSafeArea()
{
return GetSafeArea();
}
public Vector2 GetSafeAreaRatio()
{
Rect safe = GetSafeArea();
return new Vector2(
safe.width / Screen.width,
safe.height / Screen.height);
}
private void OnRectTransformDimensionsChange()
{
if (rectTransform != null)
ApplySafeArea();
}
private void OnValidate()
{
if (rectTransform == null)
rectTransform = GetComponent<RectTransform>();
if (rectTransform != null)
ApplySafeArea();
}
}