Sizing and safe areas

Learn how SkeleKit measures and arranges views without Auto Layout constraints.

SkeleKit uses a two-pass layout system. A parent first asks each child how much space it wants, then assigns its final bounds. There is no Auto Layout constraint graph to maintain.

The built-in containers apply that system in different ways; start with StackPanel and Grid for the common cases.

Requested and constrained sizes

Width and Height request explicit dimensions. Leave them unset to use the size measured from content. MinWidth, MaxWidth, MinHeight, and MaxHeight constrain either result.

new Button
{
    HorizontalAlignment = HorizontalAlignment.Stretch,
    MinHeight = 44,
    MaxWidth = 420,
    Margin = new Thickness(16, 8),
    Text = "Continue"
};

Margin belongs to every View and reserves space outside it. Padding belongs to containers and reserves space between the container's bounds and its content.

System content insets

Set a container's SystemInsetEdges to add the containing page controller's current UIKit system margins on selected edges. These insets are added to Padding; they do not change the container's own bounds or its Margin.

For scrolling pages, apply them to the panel inside the scroll view. The viewport and scroll indicators then remain edge-to-edge while the content follows UIKit's standard margins:

Content = new ScrollView
{
    Content = new StackPanel
    {
        SystemInsetEdges = LayoutEdges.Horizontal,
        Padding = new Thickness(0, 12),
        Children =
        {
            new Label { Text = "Account" },
            new TextField { Placeholder = "Name" }
        }
    }
};

CollectionView applies the same property to its compositional layout rather than its scroll-view insets, so cells, headers, and footers follow the margins without moving the scroll indicators:

Content = new CollectionView<Contact>
{
    SystemInsetEdges = LayoutEdges.Horizontal,
    ItemsSource = viewModel.Contacts,
    ItemTemplate = static () => new ContactRow()
};

Use LayoutEdges.Leading, Trailing, Top, or Bottom individually, combine them with |, or use the Horizontal, Vertical, and All groups. UIKit resolves the values from the live page, and SkeleKit updates layout if they change.

Alignment

Horizontal and vertical alignment decide where a view sits when its parent offers more room than it needs.

  • Start, Center, and End keep the measured size and position it along that axis.
  • Stretch uses the available size, subject to minimum and maximum constraints.

An explicit size still takes precedence over stretching. Layout containers may apply additional rules; for example, a vertical StackPanel stacks measured heights but still controls the horizontal slot.

Measurement state

After measurement, DesiredSize contains the requested size. After arrangement, ArrangedBounds contains the final position and size in the parent.

Normal property setters invalidate layout automatically. Call InvalidateMeasure() after changing custom state that affects a custom view's desired size. InvalidateSubtree() is intended for changes that invalidate every descendant, such as a typography environment change.

LayoutNow() forces a pending pass from the root. It is rarely needed in application code, but can be useful before reading final bounds for an immediate native operation.

Geometry values

SkeleKit uses its own small value types throughout layout:

  • Size stores width and height.
  • Point stores an x and y coordinate.
  • Rect combines an origin and size.
  • Thickness stores left, top, right, and bottom insets.

They support the usual constructors and helper operations without introducing UIKit geometry types into the shared layout API.

Safe areas

Pages keep their content inside all safe-area edges by default through ContentView.SafeAreaEdges. The ContentView itself always fills the controller, so its Background continues beneath system UI. Choose specific edges when the content should extend under part of that UI:

public PhotoPage()
{
    SafeAreaEdges = SafeAreaEdges.Leading |
                    SafeAreaEdges.Trailing |
                    SafeAreaEdges.Bottom;
    Content = new Image { Source = "hero" };
}

View.IgnoresSafeArea is a local escape hatch for an individual view. ScrollsUnderBars controls whether scrolling page content continues beneath navigation bars so UIKit can apply its normal translucent bar behavior.

A content-detent sheet has no bottom safe area on iPad, so its content would sit against the sheet's edge. ContentView.MinimumContentInsets raises the content insets on any side where the safe area and padding together fall short; the larger system inset is used, so a home indicator is never padded twice:

public MoodPage()
{
    MinimumContentInsets = new Thickness(0, 0, 0, 12);

    Content = new CollectionView<Mood>
    {
        /* … */
    };
}