SkillAgentSearch skills...

Grid

The most powerful Grid container missed in SwiftUI

Install / Use

/learn @exyte/Grid
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<a href="https://exyte.com/"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/exyte/media/master/common/header-dark.png"><img src="https://raw.githubusercontent.com/exyte/media/master/common/header-light.png"></picture></a>

<a href="https://exyte.com/"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/exyte/media/master/common/our-site-dark.png" width="80" height="16"><img src="https://raw.githubusercontent.com/exyte/media/master/common/our-site-light.png" width="80" height="16"></picture></a>     <a href="https://twitter.com/exyteHQ"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/exyte/media/master/common/twitter-dark.png" width="74" height="16"><img src="https://raw.githubusercontent.com/exyte/media/master/common/twitter-light.png" width="74" height="16"> </picture></a> <a href="https://exyte.com/contacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/exyte/media/master/common/get-in-touch-dark.png" width="128" height="24" align="right"><img src="https://raw.githubusercontent.com/exyte/media/master/common/get-in-touch-light.png" width="128" height="24" align="right"></picture></a>

<img width="480" src="https://github.com/exyte/Grid/raw/media/Assets/calc-animation-mock-iPhone-XS-Max.gif"/>

Grid

Grid view inspired by CSS Grid and written with SwiftUI

<a href="https://exyte.com/blog/implementing-grid-layout-in-swiftui">Read Article »</a>

SPM Compatible Cocoapods Compatible Carthage Compatible License: MIT

Overview

Grid is a powerful and easy way to layout your views in SwiftUI:

<img src="https://i.imgur.com/pl3k7iE.gif">

Check out full documentation below.

Installation

CocoaPods

Grid is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'ExyteGrid'

Swift Package Manager

Grid is available through Swift Package Manager.

Add it to an existing Xcode project as a package dependency:

  1. From the File menu, select Swift Packages › Add Package Dependency…
  2. Enter "https://github.com/exyte/Grid" into the package repository URL text field

Requirements

  • iOS 14.0+ (the latest iOS 13 support is in v0.1.0)
  • MacOS 10.15+
  • Xcode 12+

Building from sources

git clone git@github.com:exyte/Grid.git
cd Grid/Example/
pod install
open Example.xcworkspace/

Documentation

1. Initialization

<img align="right" width="30%" height="30%" src="https://github.com/exyte/Grid/raw/media/Assets/3-equal-fr-tracks.png"/>

You can instantiate Grid in different ways:

  1. Just specify tracks and your views inside ViewBuilder closure:
Grid(tracks: 3) {
    ColorView(.blue)
    ColorView(.purple)
    ColorView(.red)
    ColorView(.cyan)
    ColorView(.green)
    ColorView(.orange)
}
  1. Use Range:
Grid(0..<6, tracks: 3) { _ in
    ColorView(.random)
}
  1. Use Identifiable enitites:
Grid(colorModels, tracks: 3) {
    ColorView($0)
}
  1. Use explicitly defined ID:
Grid(colorModels, id: \.self, tracks: 3) {
    ColorView($0)
}

2. Containers

ForEach

Inside ViewBuilder you also can use regular ForEach statement. There is no way to get KeyPath id value from the initialized ForEach view. Its inner content will be distinguished by views order while doing animations. It's better to use ForEach with Identifiable models or GridGroup created either with explicit ID value or Identifiable models to keep track of the grid views and their View representations in animations.

<img align="right" width="30%" height="30%" src="https://github.com/exyte/Grid/raw/media/Assets/forEach-1.png">
Grid(tracks: 4) {
    ColorView(.red)
    ColorView(.purple)
    
    ForEach(0..<4) { _ in
        ColorView(.black)
    }

    ColorView(.orange)
    ColorView(.green)
}

GridGroup

Number of views in ViewBuilder closure is limited to 10. It's impossible to obtain content views from regular SwiftUI Group view. To exceed that limit you could use GridGroup. Every view in GridGroup is placed as a separate grid item. Unlike the Group view any outer method modifications of GridView are not applied to the descendant views. So it's just an enumerable container. Also GridGroup could be created by Range<Int>, Identifiable models, by ID specified explicitly.

You can bind a view’s identity to the given single Hashable or Identifiable value also using GridGroup. This will produce transition animation to a new view with the same identity.

There is no way to use View's .id() modifier as inner ForEach view clears that value

You can use GridGroup.empty to define a content absence.

Examples:

var arithmeticButtons: GridGroup {
    GridGroup {
        CalcButton(.divide)
        CalcButton(.multiply)
        CalcButton(.substract)
        CalcButton(.equal)
    }
}
var arithmeticButtons: GridGroup {
    let operations: [MathOperation] =
        [.divide, .multiply, .substract, .add, .equal]
	
    return GridGroup(operations, id: \.self) {
        CalcButton($0)
    }
}
var arithmeticButtons: GridGroup {
    let operations: [MathOperation] =
        [.divide, .multiply, .substract, .add, .equal]
	
    return GridGroup {
        ForEach(operations, id: \.self) {
            CalcButton($0)
        }
    }
}

var arithmeticButtons: GridGroup {
    let operations: [MathOperation] =
        [.divide, .multiply, .substract, .add, .equal]
    return GridGroup(operations, id: \.self) { 
         CalcButton($0)
    }
}
var arithmeticButtons: GridGroup {
    let operations: [MathOperation] =
        [.divide, .multiply, .substract, .add, .equal]
    return GridGroup(operations, id: \.self) { 
         CalcButton($0)
    }
}
Grid {
...
    GridGroup(MathOperation.clear) {
        CalcButton($0)
    }
}

3. Track sizes

There are 3 types of track sizes that you could mix with each other:

<img align="right" width="30%" height="30%" src="https://github.com/exyte/Grid/raw/media/Assets/3-const-tracks.png"/>

Fixed-sized track:

.pt(N) where N - points count.

Grid(tracks: [.pt(50), .pt(200), .pt(100)]) {
    ColorView(.blue)
    ColorView(.purple)
    ColorView(.red)
    ColorView(.cyan)
    ColorView(.green)
    ColorView(.orange)
}
<img align="right" width="30%" height="30%" src="https://github.com/exyte/Grid/raw/media/Assets/3-fit-tracks.png"/>

Content-based size: .fit

Defines the track size as a maximum of the content sizes of every view in track

Grid(0..<6, tracks: [.fit, .fit, .fit]) {
    ColorView(.random)
        .frame(maxWidth: 50 + 15 * CGFloat($0))
}

Pay attention to limiting a size of views that fills the entire space provided by parent and Text() views which tend to draw as a single line.

Flexible sized track: .fr(N)

<img align="right" width="30%" height="30%" src="https://github.com/exyte/Grid/raw/media/Assets/3-fr-tracks.png"/>

Fr is a fractional unit and .fr(1) is for 1 part of the unassigned space in the grid. Flexible-sized tracks are computed at the very end after all non-flexible sized tracks (.pt and .fit). So the available space to distribute for them is the difference of the total size available and the sum of non-flexible track sizes.

Grid(tracks: [.pt(100), .fr(1), .fr(2.5)]) {
    ColorView(.blue)
    ColorView(.purple)
    ColorView(.red)
    ColorView(.cyan)
    ColorView(.green)
    ColorView(.orange)
}

Also, you could specify just an Int literal as a track size. It's equal to repeating .fr(1) trac

View on GitHub
GitHub Stars2.1k
CategoryDevelopment
Updated3d ago
Forks101

Languages

Swift

Security Score

95/100

Audited on Mar 31, 2026

No findings