Skip to content

swifter09/StructuredLayout

Repository files navigation

StructuredLayout

English | 简体中文

CI Swift 5.9+ iOS 14+ Swift Package Manager License: MIT

SwiftUI-like structural layout for existing UIKit and SnapKit projects — without migration or extra container UIViews.

StructuredLayout adds Row, Column, HStack, VStack, ZStack, and Spacer to UIKit. It compiles a declarative layout tree into native Auto Layout constraints and remains fully interoperable with SnapKit.

Why StructuredLayout?

  • Designed for existing projects that already use SnapKit; adopt it screen by screen.
  • In the repository's representative Profile Card, layout code drops from 48 to 29 lines — 39.6% fewer.
  • Row/Column/HStack/VStack make source structure mirror the UI hierarchy.
  • Layout nodes use UILayoutGuide; they do not add container UIViews.
  • It does not replace SnapKit. Keep snp.makeConstraints for special relationships.
  • The core package has zero third-party dependencies.
  • Measured installation overhead versus SnapKit is approximately 0.076 ms per representative card in the documented Debug simulator benchmark.

The line-count result belongs to the included example, not every possible screen. Benchmark results are snapshots, not cross-device guarantees.

StructuredLayout and SnapKit rendering the same Profile Card

SnapKit vs StructuredLayout

Both implementations render the same Profile Card with the same 10 business UIViews and no layout-only container UIViews.

SnapKit 48 lines versus StructuredLayout 29 lines

Implementation Layout LOC Manual addSubview Result
SnapKit only 48 Required Baseline
StructuredLayout 29 Not required 39.6% fewer lines

The counted regions are marked with comparison-layout-start/end in both source files. Shared view creation, typography, colors, and content live in ProfileCardElements.swift and are excluded from both totals.

Installation

Xcode

Choose File → Add Package Dependencies and enter:

https://github.com/swifter09/StructuredLayout.git

Package.swift

.package(
    url: "https://github.com/swifter09/StructuredLayout.git",
    from: "0.1.5"
)

Quick Start

import StructuredLayout
import UIKit

final class ProfileView: UIView {
    private let avatarView = UIImageView()
    private let nameLabel = UILabel()
    private let detailLabel = UILabel()
    private let actionButton = UIButton(type: .system)

    override init(frame: CGRect) {
        super.init(frame: frame)

        sl.layout {
            HStack(spacing: 12, alignment: .center) {
                avatarView.size(48)

                VStack(spacing: 4) {
                    nameLabel
                    detailLabel
                }
                .expand()

                actionButton
            }
            .padding(16)
        }
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

No manual addSubview calls are required. HStack and VStack are aliases for Row and Column; both naming styles share the same layout nodes and constraint compiler.

Keep Using SnapKit

Use StructuredLayout for primary structure and SnapKit for exceptional relationships:

sl.layout {
    VStack {
        headerView
        contentView.expand()
        footerView
    }
}

floatingButton.snp.makeConstraints {
    $0.trailing.bottom.equalTo(safeAreaLayoutGuide).inset(16)
}

Avoid giving the same view conflicting size or position constraints through both systems.

SwiftUI-like Syntax, UIKit Runtime

StructuredLayout SwiftUI concept
Row / HStack HStack
Column / VStack VStack
ZStack ZStack
Spacer Spacer
.padding(), .size(), .expand() View modifiers
Result Builder if / for ViewBuilder conditions and collections

StructuredLayout is not a SwiftUI runtime. It operates on existing UIView instances and installs Auto Layout constraints once. It has no value-type View, state-driven rendering, tree diffing, Environment, or View Identity.

The accurate positioning is: a SwiftUI-like structural layout DSL for UIKit, not a reimplementation of SwiftUI.

Layout Targets

view.sl.layout(pinTo: .edges) { ... }
view.sl.layout(pinTo: .safeArea) { ... }
view.sl.layout(pinTo: .layoutMargins) { ... }
view.sl.layout(pinTo: .readableContent) { ... }
view.sl.layout(pinTo: .guide(customGuide)) { ... }

pinTo selects the outer boundary of the layout tree. .padding(...) controls its internal spacing.

LayoutHandle

Static layouts do not need to retain the returned handle:

view.sl.layout {
    VStack { contentView }
}

Retain it when you need lifecycle control:

private var layoutHandle: LayoutHandle?

layoutHandle = view.sl.layout {
    VStack { contentView }
}

layoutHandle?.deactivate()
layoutHandle?.activate()
layoutHandle?.uninstall()

uninstall() removes constraints and layout guides created by that installation while preserving business UIViews by default. It is terminal; install a new tree when another layout is needed.

Performance Benchmark

The isolated Benchmark package compares Native Auto Layout, SnapKit 6.0.0, and StructuredLayout while keeping the core package dependency-free.

Implementation 100 cards Per card Physical memory delta
Native Auto Layout 39.5 ms 0.395 ms 495 KB
SnapKit 6.0.0 48.7 ms 0.487 ms 485 KB
StructuredLayout 56.4 ms 0.564 ms 482 KB

In this Debug / iPhone 17 Simulator / iOS 26.5 snapshot, StructuredLayout adds approximately 0.076 ms per card versus SnapKit. Memory differences are within measurement noise.

XCTest benchmark output

See Benchmarks/README.md for scope, commands, and interpretation.

Current API

  • Row / HStack
  • Column / VStack
  • ZStack
  • Spacer
  • padding
  • width, height, size
  • minWidth, maxWidth, minHeight, maxHeight
  • aspectRatio
  • expand
  • natural, equalSize
  • Result Builder conditions and loops
  • LayoutHandle

Example App

Open StructuredLayoutExample.xcodeproj and run it. The segmented control switches between the StructuredLayout and SnapKit implementations of the same UI.

Roadmap

See ROADMAP.md for planned exploration around Grid and wrapping, responsive updates, constraint animation, and collection/table-cell reuse guidance.

Current Limitations

  • expand() lowers the main-axis hugging priority of leaf UIViews; it is not full CSS flex-grow.
  • No wrapping, Grid, responsive state, or layout-tree diffing yet.
  • UIViews in one installed tree must be direct subviews of the installation container.
  • Install layouts once during initialization or setupUI; do not reinstall inside every cell configure call.
  • APIs may evolve before 1.0.

Why No Yoga?

Yoga calculates frames. StructuredLayout generates Auto Layout constraints so it can interoperate naturally with SnapKit, safe areas, intrinsic content sizes, and constraint animation. The MVP intentionally keeps one layout system.

Contributing

Issues, API feedback, and focused pull requests are welcome. Real-world UIKit/SnapKit migration examples are especially valuable before 1.0.

License

StructuredLayout is available under the MIT license. See LICENSE.

About

A structured Row, Column and ZStack layout DSL for existing UIKit and SnapKit projects.

Topics

Resources

License

Stars

2 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages