πŸŽ› QGrid: The missing SwiftUI collection view.

Overview

[NOTE]
If you'd like to see QGrid in action, check out this demo of QDesigner (see video below).
Install QDesigner: https://apps.apple.com/us/app/qdesigner/id1500810470
Install a companion QDesigner Client on iPhone, to see your UI design on a target device, updated in real-time: https://apps.apple.com/us/app/qdesignerclient/id1500946484

Learn more at: https://Q-Mobile.IT/Q-Designer

QDesigner Preview

QGrid: The missing SwiftUI collection view.

β€œBuild Platforms Swift Package Manager License: MIT Twitter: @karolkulesza

QGrid is the missing SwiftUI collection view. It uses the same approach as SwiftUI's List view, by computing its cells from an underlying collection of identified data.

πŸ”· Requirements

      βœ… macOS 10.15+
      βœ… Xcode 11.0
      βœ… Swift 5+
      βœ… iOS 13+
      βœ… tvOS 13+

πŸ”· Installation

QGrid is available via Swift Package Manager.

Using Xcode 11, go to File -> Swift Packages -> Add Package Dependency and enter https://github.com/Q-Mobile/QGrid

πŸ”· Usage

✴️ Basic scenario:

In its simplest form, QGrid can be used with just this 1 line of code within the body of your View, assuming you already have a custom cell view:

struct PeopleView: View {
  var body: some View {
    QGrid(Storage.people, columns: 3) { GridCell(person: $0) }
  }
}   

struct GridCell: View {
  var person: Person

  var body: some View {
    VStack() {
      Image(person.imageName)
        .resizable()
        .scaledToFit()
        .clipShape(Circle())
        .shadow(color: .primary, radius: 5)
        .padding([.horizontal, .top], 7)
      Text(person.firstName).lineLimit(1)
      Text(person.lastName).lineLimit(1)
    }
  }
}

✴️ Customize the default layout configuration:

You can customize how QGrid will layout your cells by providing some additional initializer parameters, which have default values:

struct PeopleView: View {
  var body: some View {
    QGrid(Storage.people,
          columns: 3,
          columnsInLandscape: 4,
          vSpacing: 50,
          hSpacing: 20,
          vPadding: 100,
          hPadding: 20) { person in
            GridCell(person: person)
    }
  }
}   

πŸ”· Example App

πŸ“± QGridTestApp directory in this repository contains a very simple application that uses QGrid. Open QGridTestApp/QGridTestApp.xcodeproj and either use the new Xcode Previews feature or just run the app.

πŸ”· QGrid Designer

πŸ“± QGridTestApp contains also the QGrid Designer area view, with sliders for dynamic run-time configuration of the QGrid view (with config option to hide it). Please refer to the following demo executed on the device:

πŸ”· Roadmap / TODOs

Version 0.1.1 of QGrid contains a very limited set of features. It could be extended by implementing the following tasks:

      ☘️ Parameterize spacing&padding configuration depending on the device orientation
      ☘️ Add the option to specify scroll direction
      ☘️ Add content-only initializer to QGrid struct, without a collection of identified data as argument (As in SwiftUI’s List)
      ☘️ Add support for other platforms (watchOS)
      ☘️ Add Stack layout option (as in UICollectionView)
      ☘️ Add unit/UI tests
      ☘️ ... many other improvements

πŸ”· Contributing

πŸ‘¨πŸ»β€πŸ”§ Feel free to contribute to QGrid by creating a pull request, following these guidelines:

  1. Fork QGrid
  2. Create your feature branch
  3. Commit your changes, along with unit tests
  4. Push to the branch
  5. Create pull request

πŸ”· Author

      πŸ‘¨β€πŸ’» Karol Kulesza (@karolkulesza)

πŸ”· License

      πŸ“„ QGrid is available under the MIT license. See the LICENSE file for more info.

Comments
  • Cocoapods support?

    Cocoapods support?

    This library looks awesome, but probably not going to use it without cocoapods support. For me, it's probably not worth using two dependency managers in the same project (especially with SPM being such a new technology).

    I totally understand if you don't want to support multiple dependency managers, but if you did, you'd probably get more traction, usage, and PRs.

    opened by plivesey 4
  • Error when importing QGrid in project with lower deployment target

    Error when importing QGrid in project with lower deployment target

    I am trying to use QGrid on my iOS 10+ app with no luck.

    Compiling for iOS 10.0, but module 'QGrid' has a minimum deployment target of iOS 13.0

    I would like to import it and use it conditionally just like import SwiftUI does. Any ideas?

    Thank you

    opened by rivera-ernesto 3
  • Inconsistent state after rotation

    Inconsistent state after rotation

    I have a 1 x 8 grid in portrait mode and 2x4 grid in landscape mode. I have a button in each view in the grid that toggles using @State I set button 1 and 2 on (it change color to green). I rotate the simulator to landscape mode. columns are displayed correctly (2 x 4 grid). but now, buttons 1 and 3 show the green button.

    Looks like there's some recycling going on? Each gridview has the state internally.

    opened by quantamrhino 2
  • Wrapping the GridCell in a NavigationLink does not display the image

    Wrapping the GridCell in a NavigationLink does not display the image

    struct GridCell: View { var person: Person

    var body: some View { NavigationLink( destination: Text("") ) { VStack() { Image(person.imageName) .resizable() .scaledToFit() .clipShape(Circle()) .shadow(color: .primary, radius: 5) .padding([.top, .leading, .trailing], 7) Text(person.firstName).lineLimit(1) Text(person.lastName).lineLimit(1) } .font(.headline).foregroundColor(.white) } } }

    Screen Shot 2019-09-15 at 2 33 21 PM

    opened by alancook 2
  • Use of unresolved identifier 'UIDevice'

    Use of unresolved identifier 'UIDevice'

    Upon adding the Swift Package, I get the compile time error Use of unresolved identifier 'UIDevice'even when not using the package yet.

    The line throwing the error is

    private var cols: Int {
        UIDevice.current.orientation.isLandscape ? columnsInLandscape : columns
    }
    

    in QGrid.swift.

    Target is macOS Catalina.

    opened by melgu 2
  • Use QGrid inside another scroll view

    Use QGrid inside another scroll view

    I have a use case where I need to have a parent scroll view with some other content and then I have a QGrid view that scrolls by itself How do I handle this case as the scrolling is not intuitive I would like to know if I could do something to resolve this

    VStack{
                        ScrollView(.vertical , showsIndicators: false){
    //                        VStack(alignment: .leading, spacing: 10) {
                                BannerView()
                                CategoryRow().environmentObject(self.latestViewModel)
                                ColorRow().environmentObject(self.latestViewModel)
                        }
                                if(latestViewModel.error.isEmpty){
                                   QGrid(latestViewModel.wallpapers,columns: self.columns, vSpacing : 20.0, hSpacing: 5.0,
                                           hPadding: 10.0, endReached: {
                                            //print("End Reached")
                                            self.latestViewModel.getMoreWallpapers()
                                           }){wallpaper in
                                        self.getLatestTabItem(wallpaper: wallpaper)
                                    }
    

    Just a code snippet to demonstrate what I exactly want to do

    opened by ForceGT 1
  • On iPad, NavigationLink sometimes doesn't work in DoubleColumnNavigationViewStyle()

    On iPad, NavigationLink sometimes doesn't work in DoubleColumnNavigationViewStyle()

    When NavigationLink is embedded like this:

    CollectionView(self.Containers.containers, columns: 3) { container in
    	NavigationLink(destination: ContainerDetailView(container: container)) {
    		ContainerCell(container: container)
    		.environmentObject(self.Containers)
    		.accentColor(.primary)
    	}
    }
    

    tapping on it sometimes doesn't open NavigationLink destination. onTapGesture works always though.

    There is also another issue: when NavigationLink destination is open, NavigationLink destination from navigationBarItems opens and closes instantly after opening.

    opened by rrroyal 1
  • Demo of QGridTestApp problem

    Demo of QGridTestApp problem

    If you embed the ZStack in a List as in the following:

    var body: some View {
        GeometryReader { geometry in
            List {
                ZStack {
                    self.backgroundGradient
                        .edgesIgnoringSafeArea(.all)
                    VStack {
                        if (QConstants.showDesigner) { self.designerView(geometry) }
                        self.gridView(geometry)
                    }
                }
            }
        }
    }
    

    Then you get this in the simulator:

    Screenshot 2019-11-12 at 12 42 31

    I have tried various experiments and the best I can come up with is to add a frame modifier as in:

    var body: some View {
        GeometryReader { geometry in
            List {
                ZStack {
                    self.backgroundGradient
                        .edgesIgnoringSafeArea(.all)
                    VStack {
                        if (QConstants.showDesigner) { self.designerView(geometry) }
                        self.gridView(geometry)
                        .frame(width: geometry.size.width,
                               height: geometry.size.height)
                    }
                }
            }
        }
    }
    

    But this is still wrong because the grid is in the wrong place (see screenshot) and I can't work out how to fix it. Any ideas?

    Screenshot 2019-11-12 at 12 50 06
    opened by writing-shed 1
  • GeomertyReader seems to cause problems

    GeomertyReader seems to cause problems

    When using the GeometryReader inside of a cell, the grid is not rendered as expected. The cells are overlapping as shown in the image below.

    struct DataPoint: Identifiable{
        var id = UUID()
        var name:String
    }
    
    
    struct ContentView: View {
        var data:Array<DataPoint> = [DataPoint(name: "A"), DataPoint(name: "B"), DataPoint(name: "C"), DataPoint(name: "D"), DataPoint(name: "E"), DataPoint(name: "F"), DataPoint(name: "G"), DataPoint(name: "H"), DataPoint(name: "J"), DataPoint(name: "K"), DataPoint(name: "L"), DataPoint(name: "M")]
        
        var body: some View {
            QGrid(data,
                  columns: 3,
                  columnsInLandscape: 4,
                  vSpacing: 0,
                  hSpacing: 0,
                  vPadding: 0,
                  hPadding: 0,
                  content: {data in
                    GeometryReader{g in
                        VStack{
                            Text(data.name)
                        }.frame(width:g.size.width, height: g.size.width).background(Color.yellow)
                    }
            })
        }
    }
    
    
    Screenshot 2019-10-28 at 1 53 35

    Any suggestions how to make sure that every cell is squared, independent from the screen size?

    opened by simibac 1
  • Safe Area causing some problems when contained in a NavigationView

    Safe Area causing some problems when contained in a NavigationView

    I noticed that the QGrid only works well when not contained in a NavigationView.

    It seems to not correctly respect the safe area and cuts off part of the UI that seem to have the hight of the navigation bar.

    Here are some examples of what I mean:

    Simulator Screen Shot - iPhone 11 Pro Max - 2019-10-22 at 11 22 03

    As you can see part of the UI underneath the navigation bar is cut out. Also the lower part of the grid is cut out due to the home indicator.

    NavigationView {
        QGrid(profiles, columns: 2) { profile in
            ProfileCardView(profile: profile)
        }
    }
    

    If I add the .edgesIgnoringSafeArea([.all]) to the grid, this is what happens:

    Simulator Screen Shot - iPhone 11 Pro Max - 2019-10-22 at 11 24 24

    NavigationView {
        QGrid(profiles, columns: 2) { profile in
            ProfileCardView(profile: profile)
        }
        .edgesIgnoringSafeArea(.all)
    }
    
    opened by andreas-braun-parship 1
  • Fix macOS build issue

    Fix macOS build issue

    QGrid was failing to build on macOS because UIDevice is not available. This change makes macOS behave the same as tvOS, in that it does not support rotation.

    opened by davedelong 1
  • Warning on macOS: Invalid frame dimension (negative or non-finite)

    Warning on macOS: Invalid frame dimension (negative or non-finite)

    On macOS 11.1, when a View is loaded for the first time, the size obtained by GeometryReader is incorrect, resulting in a negative frame value, causing this warning. The real size can only be obtained the second time, as shown in the screenshot.

    Screenshot2020-12-17 15 31 46

    opened by patrick-fu 2
  • Drag and move to reorder features support

    Drag and move to reorder features support

    I like QGrid, it is a very elegant solution like collection view.

    I am looking for a SwiftUI component like LazyGrid (https://stackoverflow.com/questions/62606907/swiftui-using-ondrag-and-ondrop-to-reorder-items-within-one-single-lazygrid), but needs to be forward compatible with iOS 13.x or macOS 10.15.x.

    Does QGrid have this feature please?

    opened by donly 0
  • Content not refreshed when creating new views

    Content not refreshed when creating new views

    I have the QGrid setup to render a list of objects that are converted to views inside the QGrid’s block. When my API fetches updated data the view is rebuilt. I confirmed that the QGrid is called again, yet, it keeps rendering the initial list of views and not the newly created views. If I switch to List it works properly. Only with QGrid it doesn’t refresh. Here is the code in question:

        var body: some View { self.makeView()
            .onReceive(timer) { _ in
                self.refreshView()
            }
        }
        
        private func makeView() -> some View {
            VStack {
                    Text("Dashboard").font(.largeTitle)
                if self.circuits.count > 0 {
                    // need to use an if, b/c showing scrollable view without content will not refresh later on
                        QGrid<[CircuitInfo], CircuitCell>(self.circuits, columns: 2, columnsInLandscape: 4, vSpacing: 10, vPadding: 10, isScrollable: true) { circuit in
                            CircuitCell(circuit: circuit)
                    }
                }
                Spacer()
                HStack {
                    Text("\(self.circuitsCount())").font(.footnote).padding(5)
                    Spacer()
                    Text("Updated: \(lastUpdated)").font(.footnote).padding(5)
                }
            }
        }
    

    Any ideas as to what I might be doing wrong?

    opened by razyalov 6
  • support Core Data NSManagedObjects for initial data

    support Core Data NSManagedObjects for initial data

    Can this Data.Element : Identifiable remove from

    public struct QGrid<Data, Content> : View where Data : RandomAccessCollection, Content : View, Data.Element : Identifiable 
    

    to support Core Data NSManagedObjects

    opened by donly 0
Owner
Q Mobile
We build custom-tailored mobile apps.
Q Mobile
A flexible grid layout view for SwiftUI

?? GridStack A flexible grid layout view for SwiftUI. WWDC20 Update Apple ο£Ώ released LazyVGrid and LazyHGrid at WWDC20. If you are fine to only suppor

Peter Minarik 620 Nov 11, 2022
Display list of Marvel comic Characters and its detail view

Marvel Universe Display list of Marvel comic Characters and its detail view Installation Dependencies in this project are provided via Xcodegen (proje

null 1 Oct 19, 2021
Building An Interactive Grid View

Building An Interactive Grid View This demo shows a few techniques on how to build a re-ordable grid view based on stock UICollectionView API. Here ar

Brian Coyner 8 Nov 14, 2022
Use Yelp API to fetch restuarants around a location and show them in a table view

Yelp Use Yelp API to fetch restuarants around a location and show them in a table view - Infinite scrolling, Prefetching, Image Caching. Design Patter

null 0 Nov 1, 2021
Using UI Table View

News-App Table View와 Table view controller Table View : Table의 크기λ₯Ό 지정할 수 μžˆλ‹€. Table View Controller: μ „μ²΄μ˜ λ·°κ°€ ν•˜λ‚˜μ˜ ν…Œμ΄λΈ” Table View Table view κ΅¬μ„±μš”μ†Œ κ²°μ •ν•˜κΈ° μ–΄λ–€

Jiwon 0 Dec 9, 2021
Typed, yet Flexible Table View Controller

ConfigurableTableViewController Simple view controller that provides a way to configure a table view with multiple types of cells while keeping type s

Arek Holko 270 Oct 15, 2022
An easy-to-use UITableViewCell subclass that implements a swippable content view which exposes utility buttons (similar to iOS 7 Mail Application)

SWTableViewCell An easy-to-use UITableViewCell subclass that implements a swipeable content view which exposes utility buttons (similar to iOS 7 Mail

Christopher Wendel 7.2k Dec 31, 2022
Objective-C library for drag-n-drop of UITableViewCells in a navigation hierarchy of view controllers.

ios-dragable-table-cells Support for drag-n-drop of UITableViewCells in a navigation hierarchy of view controllers. You drag cells by tapping and hold

Anders Borum 49 Aug 23, 2022
Generic table view controller with external data processing

FlexibleTableViewController Swift library of generic table view controller with external data processing of functionality, like determine cell's reuse

Dmytro Pylypenko 9 May 20, 2018
Simple timeline view implemented by UITableViewCell

TimelineTableViewCell TimelineTableViewCell is a simple timeline view implemented by UITableViewCell. The UI design of TimelineTableViewCell is inspir

Zheng-Xiang Ke 1.3k Dec 25, 2022
A UITableView extension that enables cell insertion from the bottom of a table view.

ReverseExtension UITableView extension that enabled to insert cell from bottom of tableView. Concept It is difficult to fill a tableview content from

Taiki Suzuki 1.7k Dec 15, 2022
TableViews - Emoji Table View For iOS With Swift

TableViews Hello! This is EmojiTableView. Let me introduce you my first app when

null 0 Jan 2, 2022
SpanGrid is an enriched SwiftUI LazyVGrid that supports a number of extra features.

SpanGrid is an enriched SwiftUI LazyVGrid that supports a number of extra features.

James Sherlock 6 Dec 17, 2022
APDynamicGrid is a SwiftUI package that helps you create consistent and animatable grids.

APDynamicGrid Overview APDynamicGrid is a SwiftUI package that helps you create consistent and animatable grids. The DynamicGrid View preserves the sa

Antonio Pantaleo 29 Jul 4, 2022
This framework allows you to build Table views using UIKit with syntax similar to SwiftUI

This framework allows you to build Table views using UIKit with syntax similar to SwiftUI

Fun 60 Dec 17, 2022
A collection of missing SwiftUI components

SwiftUIKit A collection of components that will simplify and accelerate your iOS development. Components CurrencyTextField AdaptToKeyboard (not needed

youjinp 239 Nov 18, 2022
Modern-collection-view - Modern collection view for swift

Modern collection view Sample application demonstrating the use of collection vi

Nitanta Adhikari 1 Jan 24, 2022
Eazy is the missing piece in your SwiftUI and UIKit application.

Eazy is the missing piece in your SwiftUI and UIKit application. It aims at harmonizing how your views communicate with the model and vice versa in a clear and consistent way. Eazy can be used on any Apple platform.

Johan Thorell 7 Sep 18, 2022
Blueprints is a collection of flow layouts that is meant to make your life easier when working with collection view flow layouts.

Blueprints is a collection of flow layouts that is meant to make your life easier when working with collection view flow layouts. It comes

Christoffer Winterkvist 982 Dec 7, 2022