# How to Debug Layout Issues Using the `debug` and `debugColor` Properties in FrameLayoutKit

> Debug layout issues in FrameLayoutKit with the debug and debugColor properties. Visualize frame bounds alignment and sizing errors easily in development builds.

- Repository: [Nam Kennic/framelayoutkit](https://github.com/kennic/framelayoutkit)
- Tags: how-to-guide
- Published: 2026-03-05

---

**FrameLayoutKit provides the `debug` and `debugColor` properties on the base `FrameLayout` class to draw colored dashed outlines around every layout element in Debug builds, making it easy to visualize frame bounds, alignment issues, and sizing errors during development.**

FrameLayoutKit is a lightweight Swift layout framework that simplifies complex UI construction through declarative syntax. When building intricate hierarchies with nested stacks and grids, pinpointing the exact bounds of each layout element can be challenging. The framework's built-in `debug` and `debugColor` properties offer an instant visual debugging overlay that reveals the precise geometry of every frame without requiring manual logging or breakpoints.

## Understanding the Debug Properties

FrameLayoutKit exposes two developer-friendly flags that render visual boundaries directly on top of your layouts. These properties are available on all layout classes that inherit from `FrameLayout`.

### The `debug` Property

The `debug` property is a `Bool` that toggles the visualization overlay. When set to `true`, the layout draws a dashed outline around its own `bounds` rectangle. This visualization is automatically compiled out of Release builds using the `#if DEBUG` compiler directive, ensuring zero overhead in production.

### The `debugColor` Property

The `debugColor` property accepts an optional `UIColor`. If you provide a specific color, all debug outlines for that layout instance use that hue. If you leave it as `nil`, the framework automatically assigns a random color the first time the view is drawn, making it easy to distinguish overlapping frames by their distinct colors.

## Where These Properties Live in the Source Code

The debugging system is implemented across several key files in the [kennic/framelayoutkit](https://github.com/kennic/framelayoutkit) repository:

- **[`FrameLayout.swift`](https://github.com/kennic/framelayoutkit/blob/main/FrameLayout.swift)** (lines 155-170): The base class declares both properties and implements the actual drawing logic inside its overridden `draw(_:)` method.

- **[`StackFrameLayout.swift`](https://github.com/kennic/framelayoutkit/blob/main/StackFrameLayout.swift)** (lines 48-60): This container layout overrides the properties solely to propagate values down to every child layout in the hierarchy via `applyCommonAttributes(to:)`.

- **Specialized layouts**: [`GridFrameLayout.swift`](https://github.com/kennic/framelayoutkit/blob/main/GridFrameLayout.swift) (lines 84-92), [`FlowFrameLayout.swift`](https://github.com/kennic/framelayoutkit/blob/main/FlowFrameLayout.swift) (lines 70-78), and [`DoubleFrameLayout.swift`](https://github.com/kennic/framelayoutkit/blob/main/DoubleFrameLayout.swift) (lines 85-97) each forward `debug` and `debugColor` settings to their internal `FrameLayout` instances, ensuring consistent visualization across the entire layout tree.

## How Debug Visualization Works Internally

The debugging mechanism operates through a tight integration between property setters and UIKit's display system.

### Setting the Flags and Triggering Redraw

When you assign `layout.debug = true` (or use the chainable `layout.debug(true)` API), the setter in the concrete class updates its local property and immediately walks the child hierarchy to assign the same value to nested layouts. The `didSet` observer on both `debug` and `debugColor` in `FrameLayout` calls `setNeedsDisplay()`, but only within a `#if DEBUG` block. This forces UIKit to invoke `draw(_:)` on the next display pass without manual intervention.

### Drawing the Dashed Outline

Inside `draw(_:)` in [`FrameLayout.swift`](https://github.com/kennic/framelayoutkit/blob/main/FrameLayout.swift), the method first validates the drawing conditions with `guard debug, !isEmpty, bounds != .zero`. If valid, the implementation:

1. Selects a color using `debugColor ?? randomColor()`
2. Creates a `UIBezierPath` representing the view's `bounds`
3. Applies a dash pattern via `setLineDash` to create the characteristic dashed border
4. Strokes the path to render the visible outline

### Propagation Through Container Hierarchies

All container layouts, such as `StackFrameLayout` and its subclasses `HStackLayout`, `VStackLayout`, and `ZStackLayout`, override the debug properties to forward values to their children. When you set `debug = true` on the root container, the framework automatically visualizes every nested layout, instantly revealing misplaced frames, incorrect sizes, or hidden views that might otherwise be difficult to identify.

## Step-by-Step Debugging Workflow

Follow this systematic approach to identify and resolve layout issues using the debug visualization system.

### 1. Enable Debugging on the Root Layout

Initialize your top-level layout with debugging enabled to visualize the entire hierarchy:

```swift
let root = VStackLayout {
    $0.debug(true)                    // Show outlines for all nested frames
    $0.debugColor(.systemRed)         // Optional: Force a specific color
}

```

### 2. Run in a Debug Build

Execute the app in a Debug configuration. You will see a series of colored dashed rectangles matching each `FrameLayout` instance's actual bounds. If you do not see the outlines, verify that your build settings define the `DEBUG` flag.

### 3. Inspect Unexpected Outlines

Analyze the rendered rectangles to diagnose specific issues:

- **Too large or too small**: The rectangle's size reveals if a layout is expanding beyond its intended constraints. Check `minSize`, `maxSize`, and `edgeInsets` if the outline exceeds the allocated space.
- **Misaligned**: The position of the rectangle exposes translation offsets set via `translationX` or `translationY`, or incorrect `alignment` values that push content outside expected boundaries.
- **Hidden content**: If a layout's outline is present but the inner view is invisible, verify the `ignoreHiddenView` setting or confirm that `isEnabled` is not set to `false`.

### 4. Fine-Tune Colors for Complex Hierarchies

When many nested layouts overlap, assign distinct colors to separate the layers visually:

```swift
let header = HStackLayout {
    $0.debug(true).debugColor(.systemBlue)
}
let body = VStackLayout {
    $0.debug(true).debugColor(.systemGreen)
}

```

### 5. Disable Before Shipping

Remove the `debug` flag or set it to `false` before compiling for release. The drawing code is fully excluded from Release builds via conditional compilation, but clearing the property ensures clean configuration files.

## Practical Code Examples

### Basic Usage with the Chainable API

The framework provides fluent methods for configuring debug settings inline:

```swift
let stack = HStackLayout {
    $0.spacing = 8
    $0.debug(true)              // Enable visual debugging
    $0.debugColor(.orange)      // Optional: Set outline color
    $0.add(UILabel())           // Child views wrap in FrameLayout automatically
    $0.add(UIButton())
}

```

### Selective Debugging on Sub-Layouts

You can disable debugging for specific child layouts while keeping the parent visualization active:

```swift
let root = VStackLayout {
    $0.debug(true)              // Root visualizes everything by default
    $0.add {
        $0.debug(false)         // Hide debug outline for this child only
        $0.add(UIImageView(image: placeholderImage))
    }
}

```

### Direct Property Assignment

For layouts configured outside of trailing closure syntax, set the properties directly:

```swift
let footer = VStackLayout()
footer.debug = true
footer.debugColor = UIColor.purple
footer.add(UILabel())

```

### Integration with Xcode View Debugger

The debug outlines are rendered as part of the view's `draw(_:)` implementation, meaning they appear in Xcode's view debugger:

1. Run the app with `debug = true` on target layouts
2. Pause execution and open the view debugger
3. The dashed outlines appear in the hierarchy preview, making it easy to correlate a visual rectangle with the corresponding `FrameLayout` node in the tree

## Summary

- **`debug` and `debugColor`** are declared in [`FrameLayout.swift`](https://github.com/kennic/framelayoutkit/blob/main/FrameLayout.swift) and inherited by all layout classes, providing a unified debugging interface across the framework.
- The visualization renders as a dashed outline in **Debug builds only** (`#if DEBUG`), ensuring no production performance impact.
- Container layouts in [`StackFrameLayout.swift`](https://github.com/kennic/framelayoutkit/blob/main/StackFrameLayout.swift) propagate debug flags to all children, allowing a single toggle to visualize entire layout trees.
- The drawing logic in `draw(_:)` uses `setLineDash` to stroke the view's bounds with either the specified `debugColor` or a randomly assigned color.
- Use these properties to instantly identify sizing errors, alignment offsets, and hidden views without inserting manual logging or breakpoints.

## Frequently Asked Questions

### Do debug outlines appear in production App Store builds?

No. The debug drawing code is wrapped in `#if DEBUG` conditional compilation blocks. When you compile for Release, the `draw(_:)` method skips the outline rendering entirely, and the property setters do not call `setNeedsDisplay()`. This ensures zero runtime overhead in shipped applications.

### How do I set different colors for nested layout levels?

Assign distinct `debugColor` values to each layout instance in your hierarchy. Because container layouts propagate their debug settings to children, you must override the color on the specific child if you want it to differ from its parent. For example, set the root to `.systemRed`, then configure a nested `HStackLayout` with `.systemBlue` to differentiate the layers visually.

### Why don't I see outlines even with `debug = true`?

First, verify you are running a **Debug** build configuration, as the outlines are stripped from Release builds. Second, check that the layout's `bounds` are not zero and that the layout is not empty; the `draw(_:)` method guards against drawing when `isEmpty` or `bounds == .zero`. Finally, ensure the layout is actually added to the view hierarchy and visible on screen.

### Does enabling debug mode affect layout performance?

The performance impact is negligible during development. The drawing operation uses simple Core Graphics path stroking on the CPU, and the `setNeedsDisplay()` calls are throttled by the normal UIKit display cycle. Since the code is removed from Release builds, there is no shipping performance penalty. However, extremely deep hierarchies with hundreds of nested debug layouts may see minor frame drops during development, which is why the feature is intended for debugging sessions only.