Skip to content

Say hello to GridKit, a lightweight and efficient framework designed to simplify grid-based layouts in iOS apps.

License

Notifications You must be signed in to change notification settings

Devansh-Seth-DEV/GridKit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 

Repository files navigation

GridKit Logo

GridKit Framework Documentation

Overview

GridKit is a flexible and customizable grid-based UI framework for iOS, designed to help developers create dynamic layouts with ease. It provides a structured way to define grids, manage layouts, and handle interactions.

Installation

Install Framework

📦 Installation via SPM

  1. Open Xcode > File > Add Package Dependency

  2. Enter:

https://github.com/Devansh-Seth-DEV/GridKit.git
  1. Select branch or version 1.0.0 and click Add Package.

đź”— Binary Target:

If you are manually downloading from Releases, extract GridKit.xcframework.zip and add it to your project.

Topics

Classes

Structures

Getting Started


GKCell

A Subclass of UIView representing a cell in the canvas

Initializers

init?(coder: NSCoder)
init(
  frame: CGRect,
  row: Int,
  column: Int
)
init(
  row: Int,
  column: Int,
  frame: CGRect,
  cellColor: UIColor?,
  borderWidth: CGFloat?,
  borderColor: UIColor?,
  cornerRadius: CGFloat?
)

Instance Properties

  • var canAcceptDetach: Bool - Checks whether this cell can accepts more detaches of cell or not

  • var canAcceptDrop: Bool - Check whether this cell can accept more drops or not

  • var column: Int - Column at which cell is present in canvas

  • var dragable: Bool - Flag which allow a cell to be able to be dragged in canvas, by default set to false

  • var dropStorageCount: Int - Stores the maxmium number of cells currently droped on it

  • var dropable: Bool - Flag which allow a cell to take another cell onto it or in other words to allow another cell to be droped into it, by default set to false

  • var initialOrigin: CGPoint - Initial origin of cell in the canvas, updated as cell’s position changes

  • var maxDropCapacity: Int - Stores the maximum capacity to accepts the drops on this view, default is 1

  • var row: Int - Row at which cell is present in canvas


GKSpec

A structure that defines the specifications of the canvas, including the maximum number of rows and columns required to make the canvas and the size taken by single cell in the canvas

Initializers

init(
    rows: Int,
    columns: Int,
    cellSize: CGFloat,
    interCellInsets: CGFloat,
    cellColor: UIColor?,
    canvasColor: UIColor?,
    cellBorderWidth: CGFloat?,
    cellBorderColor: UIColor?,
    cellCornerRadius: CGFloat?,
    canvasCornerRadius: CGFloat?
)

Instance Properties

  • var canvasColor: UIColor? - Defines the background color of the canvas, default is set to white

  • var canvasCornerRadius: CGFloat? - Defines the corner radius of canvas, deafult is set to nil

  • var canvasHeight: CGFloat - Computes the total grid height based on the cell size and spacing between the cells.`

  • var canvasWidth: CGFloat - Computes the total grid width based on the cell size and spacing between the cells.`

  • var cellBorderColor: UIColor? - Defines the border color of the cell in canvas, default is set to black

  • var cellBorderWidth: CGFloat? - Defines the border width of the cell in canvas, default is set to nil

  • var cellColor: UIColor? - Defines the backgound color of the cell, default is set to tertiarySystemGroupedBackground color

  • var cellCornerRadius: CGFloat? - Defines the corner radius of a cell in canvas, default is set to nil

  • var cellSize: CGFloat - Defines the size of a single cell in a canvas

  • var columns: Int - Defines Maximum number of columns in a canvas

  • var interCellInsets: CGFloat - Defines the spacing between two cells in a canvas either horizontally or vertically, default is set to 0

  • var rows: Int - Defines Maximun number of rows in a canvas


GKCanvas

It is a manager to and handles cell interaction in GKLayoutView. Each cell is assigned a unique tag i.e index of the cell in the canvas depending upon the row and column in which cell in placed

Initializers

// Initializes the Canvas with a given canvas specifications.
init(spec: GKSpec)

Instance Properties

  • var gkCells: [IndexPath : GKCell] - Track all the cells to be displayed on the canvas with their positions

  • var gklayout: UIView? - Superview which display the cells

  • var gkspec: GKSpec - Specifications of the canvas

Instance Methods

Add a marker at the corner of a cell in the canvas at given position, marker will be added even if cell not exists

public func addCornerMarker(
    at indexPath: IndexPath,
    size: CGSize = CGSize(width: 4, height: 4),
    offset: (dx: CGFloat, dy: CGFloat) = (dx: 2, dy: 2),
    cornerRadius: CGFloat = 2,
    backgroundColor: UIColor? = nil,
    borderWidth: CGFloat = 0,
    borderColor: UIColor? = .clear
)

Add markers at the corner of the cell in the canvas which satisfy certain condition on row and column, marker will be added even if cell not exists

func addCornerMarkers(where: ((_ row: Int, _ col: Int) -> Bool))

Attaches the layout view after initialization

func attachLayoutView(GKLayoutView)

Calculates the frame of a cell based on its position in the canvas

func computeCellFrame(
    row: Int,
    column: Int
) -> CGRect

Computes the cell origin based on its position in the canvas

func computeCellOrigin(
    row: Int,
    column: Int
) -> CGPoint

Fill the canvas with markers

func displayMarkers()

Erase all cells from the canvas.

func erase()

Returns all the cells present in the canvas

func getAllCells() -> [GKCell]

Returns all the positions of the cell in the canvas

func getAllIndexPaths() -> [IndexPath]

Returns the cell at given index if exists

func getCell(at: IndexPath?) -> GKCell?

Returns cell at a point if exists

func getCell(atPoint: CGPoint) -> GKCell?

Returns the cell from the canvas at given row and column if exists

func getCell(
    atRow: Int,
    column: Int
) -> GKCell?

Returns the size of single cell

func getCellSize() -> CGSize

Returns the index of cell at a point if exists

func getIndexPath(forCellAt: CGPoint) -> IndexPath?

Returns the index path of a cell in canvas if exists

func getIndexPath(ofCell: GKCell) -> IndexPath

Returns the size of canvas

func getSize() -> CGSize

Checks if a cell exists at a given position.

func hasCell(
    atRow: Int,
    column: Int
) -> Bool

Inserts a cell at a specific position

func insertCell(
    atRow: Int,
    column: Int
)

Refreshes the canvas layout

func layoutCanvasCells()

Fill the canvas with cells

func populate()

Fill the canvas with cells at given positions

func populateCells(at: [(row: Int, col: Int)])

Fill the canvas with cells which satisfy certain conditions on row and column

func populateCells(where: ((Int, Int) -> Bool))

Removes a cell from a specific position

func removeCell(
    atRow: Int,
    column: Int
)

Remove cells from given position

func removeCells(at: [(row: Int, col: Int)])

Remove cells which satisfy certain condition on row and column

func removeCells(where: ((Int, Int) -> Bool))

Removes a marker from the corner of the cell in the canvas at given position, marker will be added even if cell not exists

func removeCornerMarker(from: IndexPath)

Removes markers from the corner of the cells in the canvas which satisfy certain conditions on row and column, marker will be added even if cell not exists

func removeCornerMarker(where: ((_ row: Int, _ col: Int) -> Bool))

Removes all the markers from the canvas

func removeMarkers()

Updates cell properties at a given position

func updateCell(
    atRow: Int,
    column: Int, 
    update: ((inout GKCell) -> Void)?
)

Updates the cell position in canvas to new row or column

func updateCellPosition(
    withIndexPath: IndexPath,
    to: IndexPath
)

Update cells properties at given pair of positions (row, col)

func updateCells(
    at: [(row: Int, col: Int)],
    update: ((inout GKCell) -> Void)?
)

Update cells properties which satisfy certain condition on row and column

func updateCells(
    where: ((Int, Int) -> Bool),
    update: ((inout GKCell) -> Void)?
)

Update cells properties at given positions

func updateCells(
    with: [IndexPath],
    update: ((inout GKCell) -> Void)?
)

GKLayoutView

A UIView subclass to display the cells in grid(2d-matrix) fashion, it uses GKCanvas to manage the cells

Initializers

init?(coder: NSCoder)
init(
    frame: CGRect,
    spec: GKSpec,
    in: UIView,
    masksToBounds: Bool
)
init(
    spec: GKSpec,
    in: UIView,
    masksToBounds: Bool
)

Instance Properties

  • var allowCellDragNDrop: Bool - Flag which allow the cells in the canvas to be dragged in the canvas and dropped onto another cell

  • var allowDropInEmptySpace: Bool - Flag which allow the cells in the canvas to be dropped in empty space

  • var allowPathDrawing: Bool - Flag which allow canvas to make path between two cells when finger slides from one cell to another, tapping on the cell will not work after its activation instead you can update cell properties inside updateCellOnTrace or updateCellOnTraceEnd

  • var canvas: GKCanvas - Canvas which manages the cells in the layout

  • var columns: Int - Computes the number of row in the canvas

  • var didTapCell: ((_ cell: GKCell) -> Void)? - Function to call when a cell gets tapped, allowPathDrawing should be set to false for its working Provide cell which gets tapped

  • var initialDragOrigin: CGPoint - Stores origin of cell when it begans to drag

  • var onCellDetach: ((GKCell, GKCell) -> Void)? - Function to call before detaching the cell from another cell Provides two parameters dragingCell and acceptorCell

  • var onCellDropped: ((GKCell, GKCell) -> Void)? - Function to call before droping the cell into another cell Provides two parameters dragingCell and acceptorCell

  • var pathLayers: [CAShapeLayer] - Track all the lines with the order in which they connect two cells, allowPathDrawing should be set to true for its working

  • var pathLineWidth: CGFloat - Stores the width of the line connecting two cells

  • var pathStrokeColor: UIColor? - Stores the stroke color of the line connecting two cells

  • var rows: Int - Computes the number of rows in the canvas

  • var selectedDragCell: GKCell? - Stores the cell which is currently under draging state

  • var tracedCells: [IndexPath] - Track all the cells with the order in which they are connected, allowPathDrawing should be set to true for its working

  • var updateCellOnTrace: ((GKCell, Set<UITouch>, UIEvent?) -> Void)? - Function to call when new cell gets connected, allowPathDrawing should be set to true for its working Provides three parameters cell, touch, event

  • var updateCellOnTraceEnd: ((GKCell, Set<UITouch>, UIEvent?) -> Void)? - Function to call when event to connect two cells end to update cell properties, allowPathDrawing should be set to true for its working Provides three parameters cell, touch, event

Instance Methods

Adds the subview in canvas at specific position and also add tag to them to identify them uniquely

func addSubview(
    atRow: Int,
    column: Int,
    view: UIView
)

Calls GKCanvs erase() method

func eraseCanvas()

Returns the height of canvas from starting row till ending row, if ending is not know use negative indexes to get last indexes

func getHeight(
    fromRow: Int,
    to: Int
) -> CGFloat

Returns the size of canvas from starting (row, col) till ending (row, col), if ending is not know use negative indexes to get last indexes

func getSize(
    fromGridAt: (row: Int, col: Int),
    to: (row: Int, col: Int)
) -> CGSize

Returns the width of canvas from starting column till ending column, if ending is not know use negative indexes to get last indexes

func getWidth(
    fromColumn: Int,
    to: Int
) -> CGFloat

Calls GKCanvas populate method

func populate()

Calls GKCanvas populateCells(where:) method

func populateCells(where: ((Int, Int) -> Bool))

Calls GKCanvas populateCells(at:) method

func populteCells(at: [(row: Int, col: Int)])

Calls GKCanvas removeCell(atRow:column) method

func removeCell(
    atRow: Int,
    column: Int
)

Calls GKCanvas removeCells(at:) method

func removeCells(at: [(row: Int, col: Int)])

Calls GKCanvas removeCells(where:) method

func removeCells(where: ((Int, Int) -> Bool))

Resizes the canvas size to new size

func resizeCanvas(to: CGSize)

Sets the size of subview with the size of canvas from starting (row, col) till ending (row, col), if ending is not know use negative indexes to get last indexes

func setSubviewSize(
    UIView,
    fromGridAt startCell: (row: Int, col: Int),
    to endCell: (row: Int, col: Int)
)

Calls GKCanvas updateCells(at:update:) method

func updateCells(
    at: (Int, Int),
    update: ((inout GKCell) -> Void)?
)

Calls GKCanvas updateCells(where:update) method

func updateCells(
    where: ((Int, Int) -> Bool),
    update: ((inout GKCell) -> Void)?
)

Calls GKCanvas updateCells(with:update) method

func updateCells(
    with: [IndexPath],
    update: ((inout GKCell) -> Void)?
)

Update the newly tracked cell properties at given position

func updateTracedCell(
    atIndexPath: IndexPath,
    update: ((GKCell) -> Void)?
)

How To Use

To create the GKLayoutView first create the specifications for the canvas

import UIKit
import GridKit


let gkspec = GKSpec(
    rows: 8,
    columns: 8,
    cellSize: 40,
    interCellInsets: 2,
    cellColor: .white,
    canvasColor: .clear,
    cellBorderWidth: 2,
    cellBorderColor: .systemTeal,
    cellCornerRadius: 8,
    canvasCornerRadius: 24
)

Now as we've created the canvas specifications let's create `GKLayoutView` for our View Controller. By default canvas is placed at the center of superview
override func viewDidLoad() {
  super.viewDidLoad()
  let gklayout = GKLayoutView(
      spec: gkspec,       // Canvas specifications
      in: view            // Superview in which gklayout gets displayed
  )
}

Now we've created our GKLayoutView we can place cells in it, to place cells in it call `populate()` method and RUN the app
gklayout.populate()

As you can see canvas gets filled with cells but what if you don't want all the cells, To get specific cells you can either use `populateCells` method or you can first fill the canvas then call `removeCells` method later on but it'll be time costly process as canvas first creates the cell and then removes the required cells so it is recommended to use `populateCells` for better performance
// gklayout.populate()
gklayout.populateCells(where: { (row, col) in
    return (col == 0 && row < 5) || (row == 7)
})

Now RUN the app, you'll see only the required cells are drawn which satisfy the condition


To update the cell on specific positions use `updateCells` method
gklayout.updateCells(where: { (row, col) in
    return (col == 0 && row < 5)
}, update: { cell in
    cell.backgroundColor = .systemOrange
})

To check how your canvas grid is looking you can add markers in your canvas by calling `displayMarkers()` method
gklayout.canvas.displayMarkers()

To add a subview in the canvas at specific position in grid you can use `addSubview(atRow:column:view)` method
let label = UILabel()
label.text = "Hello"
label.textAlignment = .center
label.layer.borderColor = UIColor.systemOrange.cgColor
label.layer.borderWidth = 1

// setting the size of label
gklayout.setSubviewSize(label, fromGridAt: (row: 0, col: 1), to: (row: 0, col: -2))

// adding label in canvas at row 1, column 2
gklayout.addSubview(atRow: 1, column: 2, view: label)

// Now as you've added the label in the canvas try printing tag of label
// You'll notice that a number is printed so that number denotes the index of that label where it is located
// If you add another view at same row and column then that view also get's same tag
// Tag is calculated by Row-Order Indexing
// tagValue = (currentRow * totalNumberOfColumns) + currentColumn

What if you want to add a subview in a specific cell? Here is how you can achieve that
let label = UILabel()
label.text = "Hello"
label.textAlignment = .center

if let cell = self.gklayout.canvas.getCell(atRow: self.gkspec.rows-1, column: 0) {
    label.font = .systemFont(ofSize: UIFont.preferredFont(forTextStyle: .caption1).pointSize)
    
    // You'll have to manually set the size of the label using 'bounds' property
    label.bounds.size = CGSize(width: cell.bounds.width-10, height: cell.bounds.height-10)
    
    // Setting the center of the label to the cell's center using 'bounds' property
    // Using 'bounds' here because if we use cell's center it'll calculate from its parent's coordinate environment
    label.center = CGPoint(x: cell.bounds.width/2, y: cell.bounds.height/2)
    
    cell.addSubview(label)
    
    // Now try printing the 'tag' value of label
    // You'll get the same tag value as the cell's tag value
}

Note: If you want to set the size prefer use 'bounds' property and for the position prefer 'frame' property because 'bounds' take coordination system of its own environment while 'frame' takes coordinate system of its parent environment


Easy Right! Now lets do something interesting, lets activate the drag and drop features in the cells
// Activate the drag and drop
gklayout.allowCellDragNDrop = true

// Now as you activated the features tell the canvas which cells are dragable and which are dropable
// Lets update our cell creation code
gklayout.updateCells(where: { (row, col) in
    return (col == 0 && row < 5) || (row == 7)
}, update: { cell in
    if cell.row == 7 { cell.dragable = true }    // Enable all the cells to be able to draged on row 7
    else {                                       // Enable all the cells to be able to accept the drop where (col == 0 and row < 5)
        cell.backgroundColor = .systemOrange
        cell.dropable = true
    }
})

Now RUN the app and see you can drag and drop the cells


To run specific task while droping the cell or detaching the cell you can set the properties `onCellDropped` and `onCellDetach`
gklayout.onCellDropped = { (cell: GKCell, targetCell: GKCell) in
    cell.bounds.size.width -= 10
    cell.bounds.size.height -= 10
    cell.backgroundColor = UIColor(cgColor: targetCell.layer.borderColor!)
    targetCell.backgroundColor = .white
}

gklayout.onCellDetach = { (cell: GKCell, targetCell: GKCell) in
    cell.bounds.size = self.gklayout.canvas.getCellSize()
    targetCell.backgroundColor = cell.backgroundColor
    cell.backgroundColor = .white
}

Now RUN the app and you'll be seeing a smooth animation while draging or droping the cells


But still you may have found that you are not able to drop the cell in the empty space. To drop it in empty space activate `allowDropInEmptySpace`
gklayout.allowDropInEmptySpace = true

Now you can drop the cells in empty space too


What if you want to connect two cells with a line as you move your fingers from one cell to another? You can achieve this too by activating `allowPathDrawing`
gklayout.allowPathDrawing = true

RUN the app and see you can now getting the track of cells in which you are touching them NOTE: If allowPathDrawing is active, Drag and drop features will gets deactivated as you're already keeping track of you path


You can update the cell while connecting the cells, to do this use `updateCellOnTrace` and `updateCellOnTraceEnd`
gklayout.pathStrokeColor = .systemOrange
gklayout.updateCellOnTrace = { (cell, touch, event) in
    cell.backgroundColor = self.gklayout.pathStrokeColor?.withAlphaComponent(0.5)
}

gklayout.updateCellOnTraceEnd = { (cell, touch, event) in
    cell.backgroundColor = self.gklayout.canvas.gkspec.cellColor
}

If you want to update the cell or perform certain action while clicking the cell you can set `didTapCell` handler to perform custom tasks. For this you'll have to first disable the `allowPathDrawing` property and also the drag and drop property
// gklayout.allowPathDrawing = true
// gklayout.allowCellDragNDrop = true
// gklayout.allowDragInEmptySpace = true

gklayout.didTapCell = { cell in
    print("Cell tapped at row: \(cell.row) and column: \(cell.column)")
    
    UIView.animate(withDuration: 0.5) {
        cell.backgroundColor = .systemOrange.withAlphaComponent(0.5)
    } completion: { _ in
        UIView.animate(withDuration: 0.5) {
            cell.backgroundColor = self.gklayout.canvas.gkspec.cellColor
        }
    }
}

Demo GIFs

License

GridKit is available under the MIT license. See LICENSE for more details.

Group

  • Symbol: Represents a categorization of related components within GridKit.
    • Example: GKCell, GKCanvas, GKLayoutView might be grouped under Grid Elements.
    • You can define groups such as Core Structures, Layout Management, Utilities, etc.

Example:

  • Grid Elements: GKCell, GKCanvas, GKLayoutView
  • Specifications: GKSpec
  • Interaction Handlers: Tap gestures and event handlers.

About

Say hello to GridKit, a lightweight and efficient framework designed to simplify grid-based layouts in iOS apps.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages