Attach on a GameObject with a PanelRenderer component (or under one — see auto-fetch below). Creates a UIView for the panel's visual tree.

PanelRenderer delivers its visual tree asynchronously via a reload callback — there is no root element to grab in Awake(). UIViewComponent registers that callback for you in its Awake() and constructs the UIView when the first reload fires. Until then, UIView (and everything that forwards to it: ParentElement, Fade, LunaUIManager) is null.

Inspector View

UIViewComponent Inspector

FieldTypeDescription
Panel RendererPanelRendererPanelRenderer hosting this view. Leave empty for the common cases — see auto-fetch.
Parent NamestringOptional name of a child element to treat as this view's root. Empty = auto-resolve — see view root resolution.
Focus NamestringElement to focus when this UIView becomes visible. Empty = the view root itself.
Fade DurationfloatDuration of the fade transition, in seconds. Default 0.5.
Fade In DelayfloatDelay before the fade-in animation starts, in seconds. Default 0.
Fade Out DelayfloatDelay before the fade-out animation starts, in seconds. Default 0.
Easing ModeEasingModeEasing applied during fade transitions. Default EaseOutCirc.
Transition AnimationsList<UIViewTransitionEntry>Per-fade-event animations. Each entry targets one or more elements via ElementSelector and plays its TransitionSequenceAsset with optional StepMs between matches. See UIView — Transition Animations.
DebugboolLogs PanelRenderer auto-fetch resolution at Awake, plus fade transitions and enable/disable events.

The async load model

⚠️ Query in OnUILoaded, not Awake. The visual tree arrives asynchronously, so element queries can't live in Awake()/OnEnable() — the root isn't there yet. Use one of the members below instead.

Use one of these:

MemberDescription
bool IsUILoadedTrue once the first reload callback has fired and UIView has been constructed.
event Action UILoadedFires exactly once, when UIView becomes available.
void WhenUILoaded(Action action)Runs action immediately if the UI is already loaded, otherwise on the next UILoaded emission. Saves the "if/else subscribe" pattern at every call site.
protected virtual void OnUILoaded(VisualElement root)Subclass hook, fired exactly once on the first reload, after UIView is constructed and the start-visibility snap is applied. root is the view's mount root (UIView.ParentElement), so queries scope to the view's own subtree.

Subclass pattern — override OnUILoaded:

csharp
public class MyView : UIViewComponent { private Button _button; protected override void OnUILoaded(VisualElement root) { _button = root.Q<Button>("my-button"); } }

From outside the component — WhenUILoaded:

csharp
myView.WhenUILoaded(() => { myView.UIView.AddAction(new UIViewActionEscape(Close)); });

Note: when the PanelRenderer is already mounted at the time the component awakes (e.g. a prefab instantiated under a shell whose panel has loaded), the reload callback — and therefore OnUILoaded — fires synchronously during Awake(). Code should not rely on either timing; WhenUILoaded/OnUILoaded handle both.

PanelRenderer auto-fetch

Leave the Panel Renderer field empty for the common cases:

  • Standalone prefab — auto-fetches a PanelRenderer on the same GameObject.
  • Tab body in a shell — auto-fetches a PanelRenderer on a parent GameObject (the shell). Pair with Parent Name to point at a named slot in the shell's UXML.

At Awake() the component walks its own GameObject and then every parent until it finds a PanelRenderer. Set the field explicitly only when neither the view's own GameObject nor any parent has the PanelRenderer you want. Enable Debug to log how the auto-fetch resolved.

View root resolution

The Parent Name field decides which VisualElement becomes the view's root (UIView.ParentElement):

  • Empty (default) — auto-resolves to the first child of the PanelRenderer's generated wrapper element. That child is virtually always your UXML's outer VisualElement. The wrapper itself is unsafe to animate against — PanelSettings shuffles it during first-attach, which kills any in-progress CSS transition.
  • Set — looks up the named element, preferring a non-Button match. A tab strip commonly names a button the same as its content panel; skipping Buttons makes the lookup resolve to the content element regardless of how the buttons are named.

⚠️ Multi-root UXML. If your UXML has more than one top-level element, the auto-fallback picks the first one and logs a warning — fade/transition wiring won't touch the other siblings. Set Parent Name explicitly, or wrap the UXML in a single outer VisualElement.

When the view is a navigation destination nested under a parent destination, the name lookup is scoped to the parent view's subtree — so two sibling tabs can each have their own "DetailContainer" without cross-matching.

Public Properties

NameTypeDescription
UIViewUIViewThe UIView constructed at the first PanelRenderer reload. null until then.
PanelRendererPanelRendererThe PanelRenderer hosting this view (assigned or auto-fetched).
ParentElementVisualElementThe view's root element. Forwards to UIView.ParentElement; null before the first reload.
LunaUIManagerLuna UI ManagerForwards to UIView.LunaUIManager; null before the first reload.
FadeFadeUIElementManage fade transitions of the UIView. Forwards to UIView.Fade; null before the first reload.
IsUILoadedboolTrue once UIView has been constructed.
PushArgsobjectPer-push args set by navigation right before fade-in. See Push args.
NodeNavNodeThe navigation node this view represents, or null when the view isn't a nav destination. See Navigation integration.
IsLayerPreloadedboolTrue for the single Layer-preloaded instance per node; false for multi-instance copies spawned by nav's Push path. Set by NavHost — do not mutate from consumer code.

Public Methods

Show

Start the fade-in animation. When called before UIView exists (a nav Push can land before the PanelRenderer's first reload), the fade-in is queued and runs as soon as the tree arrives.

csharp
public void Show()

Hide

Start the fade-out animation. The fade pipeline applies display: none + opacity: 0 at the element level; the GameObject stays active. No-op when UIView isn't constructed yet.

csharp
public void Hide()

WhenUILoaded

Run an action as soon as the UI is loaded — synchronously if it already is.

csharp
public void WhenUILoaded(Action action)

Push args

When a view is opened through navigation, Push(id, args) hands the view a per-push args object right before fade-in:

csharp
public object PushArgs { get; } // null when pushed without args public T GetArgs<T>() where T : class // typed read; null on mismatch public void SetPushArgs(object args) // called by nav; also usable from test/storybook code

The canonical owner of the args is UIView.PushArgs; the component forwards to it, and stashes args set before the first reload in a pending slot that drains into the UIView when it's constructed. Read args in your OnFadeInStart-driven logic or in OnUILoaded:

csharp
protected override void OnUILoaded(VisualElement root) { var args = GetArgs<MyDialogArgs>(); }

State reset on reopen

Destinations authored with ResetStateOnReopen = true ask the view to clear and re-seed its state before each reopen. Override the hook:

csharp
protected virtual void OnStateReset(object args)

args is the new push args for the upcoming push. First-time push doesn't fire this — the view's state is fresh by construction.

UIViewComponent implements INavView, so it can be a navigation destination. The link is runtime-only — whoever spawns the view (the layer host, or your own code) calls:

csharp
public void SetNode(NavNode node)

right after Instantiate. Setting a node registers the view with the nav system, applies node-authored state (StartVisible, DisableOtherViewsOnFadeIn, backdrop), and wires external-close detection so a direct Hide() still notifies nav. Passing null clears the node and unregisters.

Views without a node (shells, HUD overlays, UIPrefabLoader-spawned ephemerals) default to born visible and don't block input on other views.

See Navigation for the node graph, layers, channels, and push/pop semantics — this page only covers the view-side surface.

Lifecycle

  • Awake — auto-fetches the PanelRenderer and registers the reload callback. The UIView is not built here.
  • First reload — resolves the view root, constructs UIView, applies fade config + transitions, applies start visibility, then calls OnUILoaded(root) and raises UILoaded.
  • Subsequent reloads — PanelRenderer rebuilds its tree on enable cycles and property changes; the component re-resolves the view root and swaps it into the existing UIView (UIView.SetParentElement).
  • OnDestroy — unregisters the reload callback and disposes the UIView (which releases page registrations, input-block state, and the global registry entry).

Subclassing note: Awake and OnDestroy are protected virtual — call base.Awake() / base.OnDestroy() when you override them. The component deliberately does not use OnEnable/OnDisable for its own registration, so your subclass is free to declare them.

Settings

Theme

Light

Contrast

Material

Dark

Dim

Material Dark

System

Sidebar(Light & Contrast only)

Light
Dark

Font Family

DM Sans

Wix

Inclusive Sans

AR One Sans

Direction

LTR
RTL