SwiftUI Grid layout with custom styles

Overview

Build Status

SwiftUI Grid

SwiftUI Grid view layout with custom styles.

Features

  • ZStack based layout
  • Vertical and horizontal scrolling
  • Supports all apple platforms
  • Custom styles (ModularGridStyle, StaggeredGridStyle)
  • SwiftUI code patterns (StyleStructs, EnvironmentValues, ViewBuilder)
  • Active development for production apps

Open GridDemo.xcodeproj for more examples for iOS, macOS, watchOS and tvOS

Styles

ModularGridStyle (Default)

ScrollView {
    Grid(colors) {
        Rectangle()
            .foregroundColor($0)
    }
}
.gridStyle(
    ModularGridStyle(columns: .min(100), rows: .fixed(100))
)

StaggeredGridStyle

ScrollView {
    Grid(1...69, id: \.self) { index in
        Image("\(index)")
            .resizable()
            .scaledToFit()
    }
}
.gridStyle(
    StaggeredGridStyle(.horizontal, tracks: 8, spacing: 4)
)

Tracks

Tracks setting allows you to customize grid behaviour to your specific use-case. Both Modular and Staggered grid use tracks value to calculate layout. In Modular layout both columns and rows are tracks.

public enum Tracks: Hashable {
    case count(Int)
    case fixed(CGFloat)
    case min(CGFloat)
}

Count

Grid is split into equal fractions of size provided by a parent view.

ModularGridStyle(columns: 3, rows: 3)
StaggeredGridStyle(tracks: 8)

Fixed

Item size is fixed to a specific width or height.

ModularGridStyle(columns: .fixed(100), rows: .fixed(100))
StaggeredGridStyle(tracks: .fixed(100))

Min

Autolayout respecting a min item width or height.

ModularGridStyle(columns: .min(100), rows: .fixed(100))
StaggeredGridStyle(tracks: .min(100))

Preferences

Get item size and position with preferences

struct CardsView: View {
    @State var selection: Int = 0
    
    var body: some View {
        ScrollView {
            Grid(0..<100) { number in
                Card(title: "\(number)")
                    .onTapGesture {
                        self.selection = number
                    }
            }
            .padding()
            .overlayPreferenceValue(GridItemBoundsPreferencesKey.self) { preferences in
                RoundedRectangle(cornerRadius: 16)
                    .strokeBorder(lineWidth: 4)
                    .foregroundColor(.white)
                    .frame(
                        width: preferences[self.selection].width,
                        height: preferences[self.selection].height
                    )
                    .position(
                        x: preferences[self.selection].midX,
                        y: preferences[self.selection].midY
                    )
                    .animation(.linear)
            }
        }
    }
}

SDKs

  • iOS 13.1+
  • Mac Catalyst 13.1+
  • macOS 10.15+
  • watchOS 6+
  • Xcode 11.0+

Roadmap

  • Items span
  • 'CSS Grid'-like features

Code Contributions

Feel free to contribute via fork/pull request to master branch. If you want to request a feature or report a bug please start a new issue.

Coffee Contributions

If you find this project useful please consider becoming my GitHub sponsor.

Comments
  • Error when I view media from Photos Library

    Error when I view media from Photos Library

    I want to show, with Grid, media from Photos Library, but I have this result. Schermata 2020-03-28 alle 19 38 05

    The code is. //ContentView File

    import SwiftUI
    import Photos
    import Grid
    
    struct ContentView: View {
        
        @State private var assetsStruct = [AssetStruct]()
        
        var body: some View {
            NavigationView {
                ScrollView {
                    Grid(assetsStruct) { assetStruct in
                        NavigationLink(destination: Text(assetStruct.asset.localIdentifier)) {
                            MediaThumbView(asset: assetStruct.asset)
                        }
                    }
                }
                .onAppear {
                    self.assetsStruct = FetchMediaUtility().fetchNumLastAssetsStruct(numAssets: 20)
                }
                .gridStyle(
                    ModularGridStyle(columns: .min(100), rows: .fixed(100))
                )
                    .navigationBarTitle("Media from Photos Library", displayMode: .inline)
            }
            .navigationViewStyle(StackNavigationViewStyle())
        }
    }
    
    struct MediaThumbView: View {
        let asset:PHAsset
        var body: some View {
            let manager = PHImageManager.default()
            let requestOptions = PHImageRequestOptions()
            requestOptions.isSynchronous = true
            requestOptions.isNetworkAccessAllowed = true
            var thumbnail = UIImage()        
            manager.requestImage(for: asset, targetSize: CGSize(width: 100, height: 100), contentMode: .aspectFit, options: requestOptions, resultHandler: {(result, info)->Void in
                if let result = result {
                    print("Video o Image width: \(result.size.width) - height: \(result.size.height)")
                    thumbnail = result
                }
            })
            return Image(uiImage: thumbnail).resizable().aspectRatio(contentMode: .fit)
        }
    }
    
    

    //File FetchMediaUtility

    import Photos
    
    struct AssetStruct: Identifiable {
        let id = UUID()
        let asset:PHAsset
    }
    
    class FetchMediaUtility {
        
        // Ultimi numAssets di AssetStruct
        func fetchNumLastAssetsStruct(numAssets:Int) -> [AssetStruct] {
            let fetchOptions = PHFetchOptions()
            fetchOptions.fetchLimit = numAssets
            fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
            let fetchAssets = PHAsset.fetchAssets(with: fetchOptions)
            var phAssetStructArray = [AssetStruct]()
            for index in 0..<fetchAssets.count {
                let asset = fetchAssets.object(at: index)
                phAssetStructArray.append(AssetStruct(asset: asset))
            }
            return phAssetStructArray
        }
    }
    

    Is there something wrong in my code or something on the Grid? I hope someone will let me know how to make Grid work, because it is the only repositary that solves my problem.

    opened by CPiersigilli 29
  • NavigationLink's cell view not working inside grid

    NavigationLink's cell view not working inside grid

    Image and text rendered in the grid view Grid(viewModel.items){ item in NavigationLink(destination: BookDetail(displayData: item)){ SearchBookCell(displayData: item) } } .gridStyle( ModularGridStyle(columns: 2, rows: .fixed(200)) ).animation(.easeInOut)

    opened by nobanhasan2 13
  • How to use it in Xcode 11.1

    How to use it in Xcode 11.1

    Hello everyone, And sorry for the dumb question: How do I use this package in my app? I have Xcode 11.1 and opened the GridExamples/GridExamples.xcodeproj file. I get several errors and I cannot run any of the examples. Particularly, I want to check the iOS one. These are the errors:

    Showing Recent Issues No account for team "Z8L4G5WZR3". (Add a new account in the Accounts preference pane or verify that your accounts have valid credentials.)

    If I want to use this extension, how do I include it in my project? I know this is so silly, but I am working on my first projects with Swiftui and I don't have much experience with Github, so please be gentle... :) Thanks in advance!

    iOS 
    opened by ctellez69 13
  • Lazy load items

    Lazy load items

    This is a very useful project. 🙌 I use it to show a lot of images in a waterfall grid. However, the API I'm using uses pagination, so I need to load the next page from the API when the user scrolls to the bottom (actually before the bottom). Do you have any plans to add support for infinite grid? Or maybe hooks to enable this use-case?

    feature iOS macOS help wanted good first issue 
    opened by sindresorhus 10
  • Gridview Layout collapses after opening the view for a second time.

    Gridview Layout collapses after opening the view for a second time.

    I just don't know if SDWebImageSwiftUI causes this issue.

    Grid(self.store.state.myPostsState.posts) { index in
          RoundedURLImage(imageURL: postImageURL(uid: index.reference.documentID), borderWidth: 0)
        .scaledToFit()
         .cornerRadius(18)
          .clipped()
      }
       .animation(.easeInOut)
    
    bug iOS 
    opened by anoels 5
  • Expose boolean of whether an item is currently visible in the grid

    Expose boolean of whether an item is currently visible in the grid

    I have a large grid of NSImageView's that play animated GIFs. This is very resource intensive, so I would like to pause playing the GIF animation for items that are outside the viewport. It would be useful if Grid could somehow expose this information.

    opened by sindresorhus 5
  • GRID: gridStyle changes when there are multiple grids on the app.

    GRID: gridStyle changes when there are multiple grids on the app.

    Hello,

    First of all thank you for this library.

    I have 2 Grids in my app, the items on the first grid displays a second grid. This grids have different cell styles, when I go back from the second grid to the first one the style of the first one have changed, now it have the style of the second grid...

    This behaviour does not exists on the previous version.

    Why now on the same package there are things I am not using? Charts, Sliders and other stuff, make more sense have it separated in different packages.

    Thanks for your reply.

    bug iOS macOS 
    opened by jberlana 5
  • setting a max number of posts for a Category

    setting a max number of posts for a Category

    Hello, I'm trying to set a max of 9 posts to be displayed in the Grid. I have an array of posts that have different categories, I want each category to have a total of 9 posts. How can I do that with Grid?

    This is the code I'm using now

    `VStack(spacing: 15){

                Grid(0..<self.userDefaultData.forSalePosts.count){ i in
                    
                        if self.userDefaultData.forSalePosts[i].Category == "\(self.FirebaseRefCategory)"{
                            
                            
                                            GridCell(title: self.userDefaultData.forSalePosts[i].Title  , image: self.userDefaultData.forSalePosts[i].image)
                                            
                                            
                                            
                                        }
                    
                     
                }
            .gridStyle(StaggeredGridStyle(.vertical, tracks: 3, spacing: 10))
    
        }`
    

    Thanks, Hasan

    opened by hasanals 4
  • Scroll both vertically and horizontally same time

    Scroll both vertically and horizontally same time

    How difficult would it be to make this library be able to scroll both vertically and horizontally at the same time? I think this functionality would bring the library to another level at least for my application. Would this be overly difficult to accomplish? I would contribute to that if you think it seems doable/desirable feature @ay42

    enhancement iOS macOS 
    opened by joshuazmiller 4
  • In case the number of items is less than the number of tracks, the StaggeredGridStyle layout will collapse.

    In case the number of items is less than the number of tracks, the StaggeredGridStyle layout will collapse.

    I tried GridDemo iOS, the StaggeredGridStyle layout collapses in case the number of grid items is less than the number of tracks.

    struct StaggeredGridView: View {
        @State var showSettings: Bool = false
        @State var style = StaggeredGridStyle(.vertical, tracks: .count(2), spacing: 1)
        @State var items: [Int] = (1...69).map { $0 }
    

    These are screenshots of layout collapse.

    • axis : .vertical
    • tracks: .count(2)

    | :+1: 3 items | 🤔 2 items | | :-: | :-: | | | |

    bug design iOS macOS 
    opened by temoki 4
  • Segmentation fault 11 with Xcode 11.4 when building for release

    Segmentation fault 11 with Xcode 11.4 when building for release

    Seems there's a compiler crash in the latest Swift 5.2 compiler.

    I haven't yet tracked down exactly what the issue is, but it can be reproduced with simple code like:

    import SwiftUI
    import Grid
    
    struct Foo: Hashable {}
    
    struct GifsView: View {
    	var body: some View {
    		List([Foo()], id: \.self) { _ in
    			Text("")
    		}
    			.gridStyle(ModularGridStyle(columns: 1, rows: 1))
    	}
    }
    

    So it's definitely something in .gridStyle or GridStyle.

    You can also reproduce the crash by opening the Xcode project in this repo, selecting the "GridDemo macOS" target and and then selecting the "Product => Archive" menu item.


    I would recommend getting this project added to https://github.com/apple/swift-source-compat-suite

    opened by sindresorhus 4
Releases(1.1.0)
Owner
SpaceNation
Software development team
SpaceNation
Simple Catalyst example (Mac idiom) of a grid-based app populated with photos, with dynamic cell layout switching

Catalyst Photo Grid Simple Catalyst example (Mac idiom) of a grid-based app populated with photos that can animate its cells between two different lay

Steven Troughton-Smith 56 Nov 14, 2022
A custom layout built on top of SwiftUI's Layout API that lays elements out in multiple lines. Similar to flex-wrap in CSS, CollectionViewFlowLayout.

WrapLayout A custom layout built on top of SwiftUI's Layout API that lays elements out in multiple lines. Similar to flex-wrap in CSS, CollectionViewF

Hiroshi Kimura 6 Sep 27, 2022
Auto Layout made easy with the Custom Layout.

Auto Layout made easy with the Custom Layout. Getting started CocoaPods CocoaPods is a dependency manager for Cocoa projects. You can install it with

Malith Nadeeshan 1 Jan 16, 2022
The most powerful Grid container missed in SwiftUI

Grid Grid view inspired by CSS Grid and written with SwiftUI We are a development agency building phenomenal apps. Overview Grid is a powerful and eas

Exyte 1.2k Jan 2, 2023
An iOS app that automatically capitalizes the title of your essay. Supports MLA, APA, and Chicago styles.

Title Capitalizr for iOS An iOS app that automatically capitalizes the title of your essay. Supports MLA, APA, AP, and Chicago styles. "You probably w

John Seong 2 Jun 10, 2022
Auto Layout (and manual layout) in one line.

Auto Layout (and manual layout) in one line. Quick Look view.bb.centerX().below(view2).size(100) It’s equivalent to iOS 9 API: view.centerXAnchor.cons

Javier Zhang 74 Oct 19, 2022
AppStoreClone - Understanding the complex layout of app store using UICompositional layout in swift

AppStoreClone Understanding the complex layout of app store using UICompositiona

Dheeraj Kumar Sharma 8 Dec 28, 2022
Flow layout / tag cloud / collection view in SwiftUI.

SwiftUIFlowLayout A Flow Layout is a container that orders its views sequentially, breaking into a new "line" according to the available width of the

Gordan Glavaš 115 Dec 28, 2022
Expose layout margins and readable content width to SwiftUI's Views

SwiftUI Layout Guides This micro-library exposes UIKit's layout margins and readable content guides to SwiftUI. Usage Make a view fit the readable con

Thomas Grapperon 26 Dec 23, 2022
SwiftUI views that arrange their children in a flow layout.

SwiftUI Flow SwiftUI views that arrange their children in a flow layout. HFlow A view that arranges its children in a horizontal flow. Usage ScrollVie

Ciaran O'Brien 114 Jan 5, 2023
SwiftUI views that arrange their children in a Pinterest-like layout

SwiftUI Masonry SwiftUI views that arrange their children in a Pinterest-like layout. HMasonry A view that arranges its children in a horizontal mason

Ciaran O'Brien 88 Dec 27, 2022
Reframing SwiftUI Views. A collection of tools to help with layout.

Overview A Swift Package with a collection of SwiftUI framing views and tools to help with layout. Size readers like WidthReader, HeightReader, and on

Ryan Lintott 84 Dec 16, 2022
Arrange views in your app’s interface using layout tools that SwiftUI provides.

Composing custom layouts with SwiftUI Arrange views in your app's interface using layout tools that SwiftUI provides. Overview This sample app demonst

Apple Sample Code 0 Jun 9, 2022
BrickKit is a delightful layout library for iOS and tvOS. It is written entirely in Swift!

BrickKit is a delightful layout library for iOS and tvOS. It is written entirely in Swift! Deprecated BrickKit is being phased out at Wayfair, and the

Wayfair Tech – Archive 608 Sep 15, 2022
LayoutKit is a fast view layout library for iOS, macOS, and tvOS.

?? UNMAINTAINED ?? This project is no longer used by LinkedIn and is currently unmaintained. LayoutKit is a fast view layout library for iOS, macOS, a

LinkedIn's Attic 3.2k Dec 27, 2022
A powerful Swift programmatic UI layout framework.

Build dynamic and beautiful user interfaces like a boss, with Swift. Neon is built around how user interfaces are naturally and intuitively designed.

Mike 4.6k Dec 26, 2022
Fast Swift Views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainable. [iOS/macOS/tvOS/CALayer]

Extremely Fast views layouting without auto layout. No magic, pure code, full control and blazing fast. Concise syntax, intuitive, readable & chainabl

layoutBox 2.1k Dec 22, 2022
A declarative Auto Layout DSL for Swift :iphone::triangular_ruler:

Cartography ?? ?? Using Cartography, you can set up your Auto Layout constraints in declarative code and without any stringly typing! In short, it all

Robb Böhnke 7.3k Jan 4, 2023
An easy way to create and layout UI components for iOS (Swift version).

Introduction Cupcake is a framework that allow you to easily create and layout UI components for iOS 8.0+. It use chaining syntax and provides some fr

nerdycat 288 Oct 9, 2022