GithubHelp home page GithubHelp logo

tiennv166 / compositionalgridview Goto Github PK

View Code? Open in Web Editor NEW
16.0 2.0 2.0 17.19 MB

A library that allows arranging views in grid or list format, scrolling horizontally or vertically with just a few lines of code

Home Page: https://github.com/tiennv166/CompositionalGridView

License: MIT License

Ruby 0.61% Swift 98.78% Objective-C 0.61%
ios swift uicollectionview

compositionalgridview's Introduction

CompositionalGridView

CI Status Version License Platform

A library that allows arranging views in grid or list format, scrolling horizontally or vertically with just a few lines of code. (No longer need to worry about UITableView or UICollectionView.)

Introduction

CompositionalGridView provides several key features:

  • Item widths based on a fraction of the total available width
    • Full width for a list layout (similar to UITableView)
    • Half-width, third-width, etc. for a grid layout
  • The layout is similar to text wrapping, based on the size of each item and the screen size
  • Horizontal scrolling with one or multiple rows (similar to sarousel or paging style)
  • Capability to scroll both horizontally and vertically within a single grid view
  • Supplementary view for each section (pinned (a.k.a sticky) header/footer)
  • Self-sizing items, headers and footers
  • Ability to embed UIView/UIViewController within a cell of a grid view

Other useful features:

  • Specifying horizontal item spacing on a per-section basis
  • Specifying vertical row spacing on a per-section basis
  • Specifying section insets on a per-section basis
  • Enable/disable pull to refresh
  • Enable/disable load more
  • Event handling for cells, allowing for actions such as tapping a button inside a cell
List Layout (like table view) with self-sizing item Third-width (static height) Third-width (dynamic height)
List Layout (like table view) Third-width (static height) Third-width (dynamic height)
Normal style (like Text Wrapping) Carousel static size (Orthogonal scrolling) Carousel dynamic size (Orthogonal scrolling)
Normal style (like Text Wrapping) Carousel static size (Orthogonal scrolling) Carousel dynamic size (Orthogonal scrolling)

Allows configuring all of styles in the same view with just a few lines of code. Now we can solely focus on coding the data model, without needing to code the layout and view (table view or collection view).

Grid View Animation

Example

To run the example project, clone the repo, and open CompositionalGridView.xcodeproj from the code directory. An example app is available to showcase and allow you to test some of the features of CompositionalGridView.

Requirements

  • Deployment target iOS 13.0+
  • Swift 5+

Installation

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

pod 'CompositionalGridView'

Building a CompositionalGridView

Once you've installed CompositionalGridView into your project, getting a basic grid view working is just a few steps.

Basic Setup

Importing CompositionalGridView

At the top of the file where you'd like to use CompositionalGridView (likely a UIView or UIViewController subclass), import CompositionalGridView:

import CompositionalGridView 

Initializing a CompositionalGridView

let gridView = CompositionalGridView()

Make sure to add the gridView as a subview to another view

gridView.addTo(view)

Configuring the delegate

Set CompositionalGridView's delegate property to an object conforming to CompositionalGridViewDelegate.

gridView.setDelegate(self)

Here's an example delegate implementation:

extension ViewController: CompositionalGridViewDelegate {
    func gridViewDidTriggerReload(_ gridView: CompositionalGridView) {
        // Handle pull to refresh event
    }
    
    func gridViewDidTriggerLoadMore(_ gridView: CompositionalGridView) {
        // Handle load more event
    }
    
    func gridViewDidSelectItem(_ gridView: CompositionalGridView, item: GridItemModelConfigurable) {
        guard let item = item as? OutlineItemCellModel else { return }
        // Handle `OutlineItemCellModel` item selection event
    }
    
    func gridViewDidTriggerEvent(_ gridView: CompositionalGridView, event: GridCellEvent) {
        guard let event = event as? OutlineItemCellEvent else { return }
        // Handle a cell event inside a cell
    }
}

Configuring the grid view Settings

GridViewSettings is used to set up content insets, background color, and enable/disable pull to refresh, load more, and vertical scrolling.

let settings = GridViewSettings(isReloadEnabled: true, isLoadMoreEnabled: true)
gridView.setSettings(settings)

Setting up cells and supplementary views

The core of grid view is its item, which is defined in the GridItemModelConfigurable protocol.

public protocol GridItemModelConfigurable {
    var layoutIndex: GridLayout.Index { get }
    var identity: String { get }
    var reuseIdentifier: String { get }
    var viewType: GridLayout.ViewType { get }
    var itemSize: GridLayout.Size { get }
    var itemSpacing: CGFloat { get }
    var lineSpacing: CGFloat { get }
    func isEqualTo(_ other: GridItemModelConfigurable) -> Bool
}

and GridLayout

Item configuration options:

  • layout index (GridLayout.Index)
    • Section index (will create a new section if one does not exist for the specified index)
    • Item index (position or row in the specified section)
    • the layout type of the section
  • identity is used for unique identification
  • reuseIdentifier is used to dequeue reusable cells or supplementary views
  • itemSpacing is used to specify the horizontal spacing between items (the default value is 0)
  • lineSpacing is used to specify vertical row spacing (the default value is 0)
  • itemSize (GridLayout.Size) controls the size of each item and has 3 modes:
    • fit: the item width fits the entire container (does not support fitting the height)
    • fixed: the item has an exact size
    • estimated: used for self-sizing items with an estimated size at the beginning
  • viewType (GridLayout.ViewType) is used to determine the type of each item: cell, self handling, header or footer
    • cell: the cell linked with the class type of UICollectionViewCell
    • selfHandling: an item that embeds a UIView or UIViewController inside
    • header/footer: the supplementary view linked with the class type of UICollectionReusableView

Setting up a cell

Firstly, initialize your own collection view cell and its corresponding cell model

struct OutlineItemCellModel {
...
}
class OutlineItemCell: UICollectionViewCell {
...
}

Then, make sure that the model conforms to GridItemModelConfigurable and links it to the your cell by class type

extension OutlineItemCellModel: GridItemModelConfigurable {
    var identity: String { ... }
    var reuseIdentifier: String { "OutlineItemCell" }
    
    // The view type is .cell, which links to your cell's class type
    var viewType: GridLayout.ViewType { .cell(OutlineItemCell.self) }
    var itemSize: GridLayout.Size { .init(width: .fit, height: .estimated(60)) }
    var layoutIndex: GridLayout.Index { .init(section: GridLayout.Section(index: section, style: .normal)) }
}

Finally, make sure that the cell conforms to GridReusableViewType protocol and overrides the configure function

extension OutlineItemCell: GridReusableViewType {
    func configure(_ model: OutlineItemCellModel) {
        // configure your cell here with corresponding model
        ...
    }
}

Setting up a supplementary view

Firstly, initialize your own header/footer view (a subclass of UICollectionReusableView) and its corresponding model

struct HeaderModel {
...
}
class HeaderView: UICollectionReusableView {
...
}

Then, make sure that the model conforms to GridItemModelConfigurable and links it to the your view by class type

extension HeaderModel: GridItemModelConfigurable {
    var identity: String { ... }
    var reuseIdentifier: String { "HeaderView" }
    
    // The view type is .header (or .footer), which links to your supplementary view's class type
    var viewType: GridLayout.ViewType { .header(HeaderView.self) }
    var itemSize: GridLayout.Size { .init(width: .fit, height: .estimated(60)) }
    var layoutIndex: GridLayout.Index { .init(section: GridLayout.Section(index: section, style: .normal)) }
}

Finally, make sure that the supplementary conforms to GridReusableViewType protocol and overrides the configure function

extension HeaderView: GridSupplementaryViewConfigurable {
    func configure(_ model: HeaderModel) {
        // configure your view here with corresponding model
        ...
    }
}

Updating items for the grid view

After setting up the cell or supplementary models, it's time to update items to the grid view. Use the updateItems function to update the items for the grid view.

public func updateItems(_ items: [GridItemModelConfigurable], hasNext: Bool = false)

This function typically takes two parameters:

  • items: This is an array of the GridItemModelConfigurable type that contains the new items to be displayed in the grid view.
  • hasNext: This parameter is optional and is used to indicate whether or not there are more items to be loaded in the grid view. If this parameter is set to true, it enables the load more functionality in the grid view.
let header: HeaderModel = ...
let items: [OutlineItemCellModel] = ...
let hasNext: Bool = ...
gridView.updateItems([header] + items, hasNext: hasNext)

Note that there is no need to register the cell type or supplementary type beforehand, as this is done automatically by the grid view when the items are updated.

Embed a UIView/UIViewController into a grid view

To embed a view or a view controller into a grid view, we use one of two functions below:

public func addSelfHandlingLogicItem(_ item: SelfHandlingItemModel, viewController: UIViewController, isHidden: Bool)
public func addSelfHandlingLogicItem(_ item: SelfHandlingItemModel, view: UIView, isHidden: Bool)

and a function to show/hide a self-handling item already embeded:

public func setHiddenItem(_ isHidden: Bool, identity: String)

SelfHandlingItemModel is the model that conforms to GridItemModelConfigurable to link with the UIView/UIViewController object.

Here's an example of how to embed a view controller into a grid view:

class GridCellViewController: UIViewController {
...
}

let viewControllerItem = SelfHandlingItemModel(
    identity: "viewController-identity",
    layoutIndex: GridLayout.Index(
        section: GridLayout.Section(
            index: 2,
            style: .normal,
            contentInsets: UIEdgeInsets(top: 16, left: 16, bottom: 0, right: 16)
        )
    ),
    itemSize: GridLayout.Size(width: .fit, height: .fixed(200))
)

gridView.addSelfHandlingLogicItem(viewControllerItem, viewController: GridCellViewController(), isHidden: false)

In this example, we create a SelfHandlingItemModel instance called viewControllerItem, which describes the layout and behavior of the grid item. Then we add it to the gridView using the addSelfHandlingLogicItem function along with an instance of GridCellViewController and the boolean value false to indicate that the item should initially be visible.

Handle events of a cell

To handle an event inside a cell, you should first define your cell event that conforms to the GridCellEvent protocol. The GridCellEvent protocol is an empty protocol that you can use as a marker to indicate that a particular enum or struct is intended to be used as a cell event.

Here's an example of how to define a custom cell event:

enum OutlineItemCellEvent: GridCellEvent {
    case view(String)
}

Once you've defined your cell event, you can use it to handle events that occur inside the cell. Here's an example of how to handle a button tap event inside a cell:

class OutlineItemCell: UICollectionViewCell {
    
    var itemId: String?
    private var eventHandler: ((GridCellEvent) -> Void)?

    @IBAction private func buttonTapped(_ sender: Any) {
        itemId.flatMap { eventHandler?(OutlineItemCellEvent.view($0)) }
    }
}

Finally, ensure that your OutlineItemCell conforms to the GridReusableViewType protocol and overrides the handleEvent function:

extension OutlineItemCell: GridReusableViewType {
    func handleEvent(_ event: @escaping ((GridCellEvent) -> Void)) {
        eventHandler = event
    }
}

CompositionalGridView+Reactive

CompositionalGridView already has 2 public publishers - contentSize and contentOffset - which allow subscribers to listen to changes (using Swift Combine).

CompositionalGridView+Combine

If your project uses RxSwift instead of Combine, you can convert those publishers to Observable as shown below:

import Combine
import CompositionalGridView
import RxRelay
import RxSwift

private struct AssociatedKeys {
    static var Cancellable = "observable_cancellable"
}

extension Reactive where Base: CompositionalGridView {

    var contentSize: Observable<CGSize> {
        let size = BehaviorRelay<CGSize>(value: .zero)
        let cancellable = base.contentSizePublisher.sink { size.accept($0) }
        // Store cancellable
        objc_setAssociatedObject(size, &AssociatedKeys.Cancellable, cancellable, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        return size.asObservable()
    }
    
    var contentOffset: Observable<CGPoint> {
        let offset = BehaviorRelay<CGPoint>(value: .zero)
        let cancellable = base.contentOffsetPublisher.sink { offset.accept($0) }
        // Store cancellable
        objc_setAssociatedObject(offset, &AssociatedKeys.Cancellable, cancellable, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        return offset.asObservable()
    }
}

Author

tiennv166, [email protected]

License

CompositionalGridView is available under the MIT license. See the LICENSE file for more info.

compositionalgridview's People

Contributors

tiennv166 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

compositionalgridview's Issues

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.