How to Implement Overlapping Views in FrameLayoutKit: ZStackLayout vs isOverlapped Property

ZStackLayout forces overlapping behavior by default, while the isOverlapped property lets you toggle overlap mode on any existing StackFrameLayout subclass using the chainable overlapped(_:) method.

FrameLayoutKit provides two distinct approaches for creating overlapping views in iOS and macOS applications. Whether you need a dedicated Z-stack container or want to enable overlap on an existing horizontal or vertical stack, understanding the difference between ZStackLayout and the isOverlapped property is essential for building layered user interfaces.

Understanding the Overlapping Views Architecture

The isOverlapped Flag

Located in FrameLayoutKit/Classes/StackFrameLayout.swift at line 22, the isOverlapped property is defined as:

public var isOverlapped: Bool = false { didSet { setNeedsLayout() } }

When set to true, the layout engine treats every child as occupying the full container dimensions. The sizeThatFits and layoutSubviews methods check this flag (visible at line 19 in sizeThatFits, lines 20-23 in the vertical branch, and lines 21-23 in the horizontal branch) and skip normal stacking calculations, aligning each subview to the container's origin.

ZStackLayout Implementation

The ZStackLayout class, found at lines 96-100 in FrameLayoutKit/Classes/StackFrameLayout.swift, is a concrete subclass that hardcodes overlapping behavior:

public class ZStackLayout: StackFrameLayout {
    public override init() {
        super.init()
        axis = .vertical
        isOverlapped = true
    }
}

Additionally, its overridden add(_:) method at lines 16-18 automatically makes each child flexible by calling super.add(view).flexible(), ensuring children stretch to fill the available space.

Implementing Overlapping Views with ZStackLayout

For simple overlapping scenarios, use the ZStackLayout or its DSL wrapper ZStackView:

// ZStackView is a DSL wrapper around ZStackLayout
let overlapping = ZStackView {
    UILabel()                // first child – placed at origin
    UIImageView(image: img)  // second child – drawn on top of the label
    UIButton(type: .system)  // third child – topmost layer
}

Under the hood, ZStackView inherits from ZStackLayout, which forces isOverlapped = true during initialization. The DSL builder calls the overridden add(_:) method, making every child flexible so they all share the same frame dimensions. Because overlapping is enabled, the layout engine skips spacing calculations and positions each child at the container's origin, creating the classic Z-order stacking effect where later children render on top of earlier ones.

Enabling Overlap on Existing Stacks with isOverlapped

If you already use HStackLayout or VStackLayout and need to toggle overlapping behavior, set the isOverlapped property directly:

let overlappedH = HStackLayout()
    .overlapped(true)          // chainable setter
    .add(UILabel())
    .add(UIImageView(image: img))
    .add(UIButton(type: .system))

The overlapped(_:) method, defined at line 28 in FrameLayoutKit/Classes/Extensions/StackFrameLayout+Chainable.swift, provides a fluent interface:

@discardableResult public func overlapped(_ value: Bool) -> Self {
    self.isOverlapped = value
    return self
}

Unlike ZStackLayout, enabling isOverlapped on a standard stack does not automatically make children flexible. You must explicitly call .flexible() on each child if you want them to stretch and fill the container:

stack.add(view).flexible()

If you prefer a DSL-style block, use the generic StackFrameLayout DSL wrapper and set the flag inside the block:

let overlappedV = VStackView {
    UILabel()
    UIImageView(image: img)
    UIButton(type: .system)
}
.overlapped(true)        // works because VStackView inherits from StackFrameLayout

When to Use Each Approach for Overlapping Views

Choose the appropriate method based on your architectural requirements:

  • Use ZStackLayout when you need a dedicated container for overlapping views. It is self-documenting, forces overlap by default, and automatically makes children flexible. Ideal for layered UI elements like badges on icons or background overlays.

  • Use isOverlapped when you need to toggle overlapping behavior dynamically on an existing HStackLayout or VStackLayout. This avoids refactoring your layout hierarchy and allows runtime switching between stacked and overlapped arrangements.

  • Combine approaches by nesting a ZStackLayout inside a normal stack to isolate overlapping regions within a larger non-overlapping layout.

Key Source Files and Implementation Details

Understanding the implementation helps debug layout issues:

File Key Implementation
FrameLayoutKit/Classes/StackFrameLayout.swift Defines isOverlapped at line 22 with didSet { setNeedsLayout() }. Contains ZStackLayout subclass at lines 96-100 and the flexible add(_:) override at lines 16-18.
FrameLayoutKit/Classes/StackFrameLayout.swift Layout logic branches for isOverlapped in sizeThatFits (line 19) and layoutSubviews (vertical branch lines 20-23, horizontal branch lines 21-23).
FrameLayoutKit/Classes/Extensions/StackFrameLayout+Chainable.swift Provides overlapped(_:) at line 28 for fluent API usage.

Both approaches rely on the same underlying engine: when isOverlapped is true, the layout skips spacing calculations and positions each child at the container's origin, creating the overlapping effect.

Summary

  • ZStackLayout provides a purpose-built solution for overlapping views, automatically setting isOverlapped = true and making all children flexible.
  • isOverlapped is a boolean flag available on all StackFrameLayout subclasses that toggles overlap mode, accessible via the chainable overlapped(_:) method.
  • When isOverlapped is enabled, the layout engine treats every child as occupying the full container frame, skipping normal stacking calculations.
  • Choose ZStackLayout for dedicated Z-axis containers; use isOverlapped to dynamically enable overlap on existing horizontal or vertical stacks.

Frequently Asked Questions

What is the difference between ZStackLayout and setting isOverlapped on a VStackLayout?

ZStackLayout is a specialized subclass that hardcodes axis = .vertical and isOverlapped = true during initialization, and automatically makes every added child flexible via its overridden add(_:) method. Setting isOverlapped on a VStackLayout only changes the layout behavior to overlap; it does not modify child flexibility or axis settings, giving you more manual control but requiring explicit configuration for each child.

Does enabling isOverlapped affect how children are sized?

Yes. When isOverlapped is true, the layout engine defined in FrameLayoutKit/Classes/StackFrameLayout.swift skips normal stacking mathematics and treats each child as occupying the full container bounds. In ZStackLayout, children are automatically made flexible. When manually enabling isOverlapped on other stacks, you must explicitly call .flexible() on each child if you want them to stretch and fill the container dimensions.

Can I toggle overlapping behavior at runtime?

Yes. Because isOverlapped is defined in FrameLayoutKit/Classes/StackFrameLayout.swift with a didSet observer that calls setNeedsLayout(), you can change its value at any time. The chainable overlapped(_:) method returns Self, allowing you to modify the flag in response to user interactions or state changes, with the layout automatically invalidating and re-rendering on the next layout cycle.

Where is the overlapping logic actually implemented in the source code?

The core logic resides in FrameLayoutKit/Classes/StackFrameLayout.swift. The isOverlapped property is defined at line 22. The layout engine checks this flag in sizeThatFits (around line 19) and layoutSubviews (in both vertical and horizontal branches, lines 20-23 and 21-23 respectively), skipping spacing calculations and aligning children to the container origin when enabled.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →