Toast Swipe Dismiss Manipulator
Gesture manipulator that allows swiping a toast/notification to dismiss it.

Contents
1 Overview
5 Methods
6 Usage
Overview
ToastSwipeDismissManipulator is a PointerManipulator that adds swipe-to-dismiss gesture recognition to any VisualElement. It supports both horizontal and vertical dismiss axes. The gesture axis is locked after 12 px of movement along the dominant direction. Dismiss triggers when travel exceeds 80 px or velocity exceeds 1 100 px/s. Incomplete swipes snap back in 140 ms; dismissed elements animate out in 180 ms.
All geometry values (dismiss travel distances, offset target) are provided as delegates evaluated lazily at gesture time, so the manipulator does not need to be re-created when layout changes.
Typical use cases:
- Swipe-to-dismiss notification toasts
- Dismissible alert or info banners
- Swipeable items in a list or feed
Behavior Constants
| Constant | Value | Description |
|---|---|---|
| Tap slop | 8 px | Maximum movement before a tap becomes a drag. |
| Swipe axis lock threshold | 12 px | Movement required along the dominant axis before the axis is locked. |
| Swipe dismiss threshold | 80 px | Travel distance that triggers a dismiss. |
| Dismiss velocity threshold | 1 100 px/s | Pointer velocity that triggers a dismiss regardless of travel distance. |
| Snap-back duration | 140 ms | Animation duration for returning to the origin position. |
| Dismiss duration | 180 ms | Animation duration for the final dismiss slide-out. |
USS Classes
This manipulator does not add USS classes. Visual feedback is applied by translating the getOffsetTarget element inline.
Events / Callbacks
All behavior is communicated through constructor-provided delegates.
| Delegate parameter | Description |
|---|---|
Func<bool> canInteract |
Called on pointer-down. Return false to ignore the gesture entirely. |
Func<Vector2, bool> canStartAtPosition |
Called with the pointer position. Return false to suppress gesture start (e.g. when a button lives in part of the toast). |
Func<float> getHorizontalDismissTravelDistance |
Returns the horizontal distance in pixels required to trigger dismiss. |
Func<float> getVerticalDismissTravelDistance |
Returns the vertical distance in pixels required to trigger dismiss. |
Func<VisualElement> getOffsetTarget |
Returns the element that will be translated during the swipe. Typically the root toast element. |
Action onInteractionStarted |
Called when the gesture crosses tap slop and is confirmed as a drag. |
Action onInteractionAborted |
Called when a drag that was started does not meet dismiss criteria and the snap-back completes. |
Action onTapped |
Called when the pointer is released within tap slop (no significant movement). |
Action onDismissed |
Called after the dismiss animation completes. Dispose of the toast element here. |
Methods
| Signature | Description |
|---|---|
ResetState() |
Cancels any in-progress gesture and immediately restores the offset target to its origin position. Call this if the toast is hidden or replaced externally while a swipe is in progress. |
Usage
This control is created from code and has no UXML element or editor menu entry.
Attach the manipulator to a toast element from code, e.g. element.AddManipulator(new ToastSwipeDismissManipulator(...)). As a manipulator it has no visuals of its own; style the host element as usual.
Using the Control
Swipe-to-Dismiss Toast
using UnityEngine;
using UnityEngine.UIElements;
using UnityUIToolkit.Extensions;
public class ToastController : MonoBehaviour
{
[SerializeField] private UIDocument _document;
private VisualElement _toastRoot;
private ToastSwipeDismissManipulator _dismissManipulator;
private void OnEnable()
{
var root = _document.rootVisualElement;
ShowToast("Session saved successfully.");
}
private void ShowToast(string message)
{
_toastRoot = new VisualElement();
_toastRoot.AddToClassList("toast");
var label = new Label(message);
_toastRoot.Add(label);
_dismissManipulator = new ToastSwipeDismissManipulator(
canInteract: () => true,
canStartAtPosition: _ => true,
getHorizontalDismissTravelDistance: () => 80f,
getVerticalDismissTravelDistance: () => 80f,
getOffsetTarget: () => _toastRoot,
onInteractionStarted: () => Debug.Log("Swipe started"),
onInteractionAborted: () => Debug.Log("Swipe aborted — snapping back"),
onTapped: () => Debug.Log("Toast tapped"),
onDismissed: RemoveToast
);
_toastRoot.AddManipulator(_dismissManipulator);
_document.rootVisualElement.Q<VisualElement>("toastLayer").Add(_toastRoot);
}
private void RemoveToast()
{
_toastRoot?.RemoveFromHierarchy();
_toastRoot = null;
}
// Call this if you need to forcibly clear the toast without waiting for a gesture
public void ForceClose()
{
_dismissManipulator?.ResetState();
RemoveToast();
}
}
Video Demo
Example Scenes
This control is demonstrated in the following package examples:
Credits and Donation
SimonDarksideJ
External links
| UI Toolkit Extensions repository | OpenUPM package |
Demonstrated In
- Content Explorer — A full content-browsing screen that composes scroll-snap, collapsible sections, inputs, a loading state and toasts into one layout.
- Toast Notifications — Queued toast messages with swipe-to-dismiss gesture handling.