Platformsios 9.3, tvos 10.2, osx 10.12

Hagrid: Grid layout library for iOS, tvOS and macOS

Cocoapods Compatible
Carthage compatible

Brings a proper grid layout to the Apple platforms!

HagridHagridHagrid Hagrid

Use gridView.config.displayGrid = true to enable grid layout as per screenshots

Available components

  • GridView – Basic grid view
  • GridTableViewCell – UITableViewCell subclass with gridView being placed on contentView (not available on macOS)
  • GridScrollView – Grid view with all the properties available to a UIScrollView (not available on macOS)
  • GridViewController – Basic Grid view based UIViewController (not available on macOS)
  • GridScrollViewController – Scroll grid view enabled view controller (not available on macOS)


// Add an album cover from the first column to 5
// (please note we are using just a plain Int instead of the Position object below)
// To make the album square we have used a custom closure with SnapKit's own `ConstraintMaker` (make) at the end of the method
gridView.add(subview: albumCover, space: 5) { make in

// Add album title label, stretching (12px padding) from the album cover image to the end of the grid view
gridView.add(subview: albumTitleLabel, from: .relation(albumCover, margin: 12), space: .last)

// Reusable position for the remaining two album labels. iPhones in portrait will take the rest of the screen,
// on bigger devices or in landscape mode these labels finish two columns from the end of the grid view (.reversed(2)). 
let subtitlesLast: Position = .custom { traitCollection in
    return self.gridView.bounds.size.width <= 414 ? .last : .reversed(2)

// Place artist label below the album title with margin of 2px, match the left position of the album title label
// and the previously calculated `subtitlesLast` for end position
gridView.add(subview: artistLabel, .below(albumTitleLabel, margin: 2), from: .match(albumTitleLabel), space: subtitlesLast)

// Place year released label under the artist name label, rest is same as above
gridView.add(subview: yearLabel, .below(artistLabel, margin: 2), from: .match(artistLabel), space: subtitlesLast)

// Place a purchase button next to the artist and year label on bigger screens and under on smaller ones (iPhone in portrait etc)
// For the start position you either start 12px from the album on smaller screens or second column from the right on bigger ones
// (note you can also use `UITraitCollection` in both cases if needed)
gridView.add(subview: purchaseButton, .custom({ traitCollection in
    if self.gridView.bounds.size.width <= 414 {
        return .below(self.yearLabel, margin: 3)
    } else {
        return .match(self.artistLabel)
}), from: .custom({ _ in
    if self.gridView.bounds.size.width <= 414 {
        return .relation(self.albumCover, margin: 12)
    } else {
        return .relation(self.artistLabel, margin: 6)
}), space: .last) { make in

// Place the star rating label above the separator. The initial position is not set and will be dynamic, the whole thing will stretch to the end
gridView.add(subview: ratingLabel, .above(separator, margin: 12), from: .dynamic, space: .last) { make in

// Add a 1px separator below any of the components that could reach it and space it 12px from the lowest one
gridView.add(subview: separator, .below([albumCover, yearLabel, purchaseButton], margin: 12)) { make in

// Add the long description copy which takes the full width and is placed below the separator
gridView.add(subview: aboutLabel, .below(separator, margin: 12))


There is a number of ways to customise your grid views:

Number of columns

Each grid view can have any number of columns, it is completely up to the developer to decide how many they want to use on which view. The default value is 12.

// Set number of columns
gridView.config.numberOfColumns = 24

Grid view outer padding

You can set top, left or right padding for the entire grid view like this:

gridView.config.padding = .full(top: 6, left: 12, right: 12)

Dynamic sizing

You can enable dynamic sizing for the grid view by setting:

gridView.config.automaticVerticalSizing = true



Available paddings are:

/// None

/// Top

/// Left

/// Right

/// Sides (left, right)
.horizontal(left: CGFloat, right: CGFloat)

/// Full (top, left, right, bottom)
.full(top: CGFloat, left: CGFloat, right: CGFloat)

Vertical positioning

For vertical positioning you set a pixel value directly (Int or Float, later will be converted to .exactly(fromTop: CGFloat)) or use one of the following values:

/// Top of the grid

/// Exact value from the top
.exactly(fromTop: CGFloat)

/// Match top of another view
.match(UIView, margin: CGFloat = 0)

/// Maintains a position under a set of elements
.below([UIView], margin: CGFloat = 0)

/// Below another view (view, margin)
.below(UIView, margin: CGFloat = 0)

/// Above another view (view, margin)
.above(UIView, margin: CGFloat = 0)

/// Custom vertical position for a given size class (trait collection, not available on macOS)
.custom(((_ traitCollection: UITraitCollection) -> Vertical))

Horizontal positioning

For horizontal positioning you can use an integer (Int) directly to specify the exact column or use one of the following methods:

/// First column

/// Specific column on the grid

/// Last column on a grid view

/// N-th column from the end

/// Up to another element
.relation(UIView, margin: CGFloat = 0)

/// Match position of another view
.match(UIView, margin: CGFloat = 0)

/// Dynamic position

/// Custom position for a given size class (trait collection, not available on macOS)
.custom(((_ traitCollection: UITraitCollection) -> Position))

View controllers

A GridViewController and GridScrollViewController are available for subclassing and your convenience with gridView property replacing the standard view.

class MyViewController: GridViewController { }


To enable a background grid do the following:

// Display grid
gridView.config.displayGrid = true

Boost – an OpenSource Enterprise AppStore

Core package for Boost, a completely open source enterprise AppStore written in Swift!

Code contributions

We love PR’s, we can’t get enough of them … so if you have an interesting improvement, bug-fix or a new feature please don’t hesitate to get in touch. If you are not sure about something before you start the development you can always contact our dev and product team through our Slack.



Ondrej Rafaj (@rafiki270 on Github, Twitter, LiveUI Slack and Vapor Slack)


To the SnapKit team for such an amazing tool!


MIT license; See the LICENSE file for more info.

Latest podspec

    "name": "Hagrid",
    "version": "1.0.2",
    "summary": "Brings proper grid layout to the Apple platforms!",
    "swift_version": "4.1",
    "description": "Hagrid is a simple to use grid layout engine based on SnapKit. Written purely in swift and with a great amount of love!",
    "homepage": "",
    "license": {
        "type": "MIT",
        "file": "LICENSE"
    "authors": {
        "rafiki270": "[email protected]"
    "source": {
        "git": "",
        "tag": "1.0.2"
    "platforms": {
        "ios": "9.3",
        "tvos": "10.2",
        "osx": "10.12"
    "source_files": "Classes/**/*",
    "dependencies": {
        "SnapKit": [
            "~> 4.0.0"

Pin It on Pinterest

Share This