A simple integrated version of iOS 13 Compositional Layout, modified into a way similar to Functional Programming to generate UICollectionViewCompositionalLayout.

Overview

WWCompositionalLayout

Swift-5.5 iOS-13.0 Swift Package Manager-SUCCESS LICENSE

A simple integrated version of iOS 13 Compositional Layout, modified into a way similar to Functional Programming to generate UICollectionViewCompositionalLayout.

iOS 13 Compositional Layout的簡單整合版,修改成類似Functional Programming的方式來生成UICollectionViewCompositionalLayout。

Installation with Swift Package Manager

dependencies: [
    .package(url: "https://github.com/William-Weng/WWCompositionalLayout.git", .upToNextMajor(from: "1.0.0"))
]

Example

import UIKit
import WWPrint
import WWCompositionalLayout

final class ViewController: UIViewController {
    
    @IBOutlet weak var myCollectionView: UICollectionView!
    
    private let badgeViewKey = "Badge"
    private let contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)
    private let edgeInsets = NSDirectionalEdgeInsets(top: 2, leading: 2, bottom: 2, trailing: 2)
    private let backgroundInsets = NSDirectionalEdgeInsets(top: 2, leading: 2, bottom: 2, trailing: 2)
    private let firstBadgeSetting: WWCompositionalLayout.BadgeSetting = (key: "Badge", size: (width: .absolute(20), height: .absolute(20)), zIndex: 100,
containerAnchor: (edges: [.top, .leading], absoluteOffset: CGPoint(x: 10, y: 10)), itemAnchor: (edges: [.bottom, .trailing], absoluteOffset: CGPoint(x: 0, y: 0)))
    
    private var currentLayoutIndex = 0
    
    enum LayoutType: Int, CaseIterable {
        case tableView
        case photoAlbum
        case bookshelf
        case vendingMachine
        case dynamicHeight
        case complexGroup
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initSetting()
    }
    
    /// 更新Layout
    /// - Parameter sender: UIBarButtonItem
    @IBAction func changeLayout(_ sender: UIBarButtonItem) {
        currentLayoutIndex += 1
        if (currentLayoutIndex > (LayoutType.allCases.count - 1)) { currentLayoutIndex = 0 }
        initSetting()
    }
}

// MARK: UICollectionViewDataSource
extension ViewController: UICollectionViewDataSource {

    func numberOfSections(in collectionView: UICollectionView) -> Int { return 10 }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return MyCollectionViewCell.dataSource.count }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView._reusableCell(at: indexPath) as MyCollectionViewCell
        cell.configure(with: indexPath)
        
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
                
        if kind == "\(WWCompositionalLayout.ReusableSupplementaryViewKind.header)" {
            let header = collectionView._reusableSupplementaryView(at: indexPath, ofKind: .header) as MyCollectionReusableHeader
            header.configure(with: indexPath)
            return header
        }
        
        if kind == "\(WWCompositionalLayout.ReusableSupplementaryViewKind.footer)" {
            let header = collectionView._reusableSupplementaryView(at: indexPath, ofKind: .footer) as MyCollectionReusableHeader
            header.configure(with: indexPath)
            return header
        }
        
        let badge = collectionView._reusableSupplementaryView(at: indexPath, ofKind: .badge(key: badgeViewKey)) as MyCollectionReusableBadge
        badge.configure(with: indexPath)
        
        return badge
    }
}

// MARK: UICollectionViewDataSource
extension ViewController: UICollectionViewDelegate, UINavigationControllerDelegate {}

// MARK: 小工具
extension ViewController {
    
    /// 初始化設定
    private func initSetting() {
        
        guard let layoutType = LayoutType.allCases[safe: currentLayoutIndex],
              let layout = layoutMaker(with: layoutType)
        else {
            return
        }

        title = "\(layoutType)"
        myCollectionView._delegateAndDataSource(with: self)
        myCollectionView.setCollectionViewLayout(layout, animated: true)
    }
    
    /// Layout選擇器
    /// - Parameter type: LayoutType
    /// - Returns: UICollectionViewCompositionalLayout?
    private func layoutMaker(with type: LayoutType) -> UICollectionViewCompositionalLayout? {
        
        switch type {
        case .tableView: return tableViewLayout()
        case .photoAlbum: return photoAlbumLayout()
        case .bookshelf: return bookshelfLayout()
        case .vendingMachine: return vendingMachineLayout()
        case .dynamicHeight: return dynamicHeightLayout()
        case .complexGroup: return complexGroupLayout()
        }
    }
}

// MARK: - CompositionalLayout
extension ViewController {
    
    /// 長得像UITableView的Layout
    /// - Returns: UICollectionViewCompositionalLayout?
    private func tableViewLayout() -> UICollectionViewCompositionalLayout? {
        
        let layout = WWCompositionalLayout.shared
            .addItem(width: .fractionalWidth(1.0), height: .absolute(120), contentInsets: edgeInsets, badgeSetting: firstBadgeSetting)
            .setDecoration(with: backgroundInsets)
            .setGroup(width: .fractionalWidth(1.0), height: .absolute(120), scrollingDirection: .horizontal)
            .setSection(with: .none, contentInsets: contentInsets)
            .setHeader(width: .fractionalWidth(1.0), height: .absolute(16))
            .setFooter(width: .fractionalWidth(0.5), height: .absolute(16))
            .build()
        
        return layoutRegister(layout)
    }
    
    /// 長得像相簿的Layout
    /// - Returns: UICollectionViewCompositionalLayout?
    private func photoAlbumLayout() -> UICollectionViewCompositionalLayout? {
        
        let layout = WWCompositionalLayout.shared
            .addItem(width: .fractionalWidth(1/3), height: .absolute(120), contentInsets: edgeInsets)
            .setDecoration(with: backgroundInsets)
            .setGroup(width: .fractionalWidth(1.0), height: .absolute(120), scrollingDirection: .horizontal)
            .setSection(with: .none, contentInsets: contentInsets)
            .setHeader(width: .fractionalWidth(1.0), height: .absolute(16))
            .setFooter(width: .fractionalWidth(0.5), height: .absolute(16))
            .build()
        
        return layoutRegister(layout)
    }
    
    /// 長得像書櫃的Layout
    /// - Parameter count: 一頁要顯示幾本
    /// - Returns: UICollectionViewCompositionalLayout?
    private func bookshelfLayout(with count: CGFloat = 4.0) -> UICollectionViewCompositionalLayout? {
        
        let mainScreenWidth = UIScreen.main.bounds.width
        let contentInsets = NSDirectionalEdgeInsets(top: 5, leading: mainScreenWidth/2 - mainScreenWidth/2/count, bottom: 5, trailing: mainScreenWidth/2/count)
        
        let layout = WWCompositionalLayout.shared
            .addItem(width: .fractionalWidth(1.0), height: .absolute(120), contentInsets: edgeInsets, badgeSetting: nil)
            .setDecoration(with: backgroundInsets)
            .setGroup(width: .fractionalWidth(1/count), height: .absolute(120), scrollingDirection: .vertical)
            .setSection(with: .continuousGroupLeadingBoundary, contentInsets: contentInsets)
            .setHeader(width: .fractionalWidth(1.0), height: .absolute(16))
            .setFooter(width: .fractionalWidth(0.5), height: .absolute(16))
            .build()
        
        return layoutRegister(layout)
    }
    
    /// 長得像自動販賣機的Layout
    /// - Returns: UICollectionViewCompositionalLayout?
    private func vendingMachineLayout() -> UICollectionViewCompositionalLayout? {
        
        let layout = WWCompositionalLayout.shared
            .addItem(width: .fractionalWidth(1.0), height: .absolute(50), contentInsets: edgeInsets, badgeSetting: nil)
            .addItem(width: .fractionalWidth(1.0), height: .absolute(100), contentInsets: edgeInsets, badgeSetting: nil)
            .addItem(width: .fractionalWidth(1.0), height: .absolute(150), contentInsets: edgeInsets, badgeSetting: nil)
            .setDecoration(with: backgroundInsets)
            .setGroup(width: .fractionalWidth(1/2), height: .estimated(100), scrollingDirection: .vertical)
            .setSection(with: .continuousGroupLeadingBoundary, contentInsets: contentInsets)
            .setHeader(width: .fractionalWidth(1.0), height: .absolute(16))
            .setFooter(width: .fractionalWidth(0.5), height: .absolute(16))
            .build()
        
        return layoutRegister(layout)
    }
    
    /// 動態高度的Layout
    /// - Returns: UICollectionViewCompositionalLayout?
    private func dynamicHeightLayout() -> UICollectionViewCompositionalLayout? {
        
        let layout = WWCompositionalLayout.shared
            .addItem(width: .fractionalWidth(1.0), height: .estimated(120), contentInsets: edgeInsets, badgeSetting: firstBadgeSetting)
            .setGroup(width: .fractionalWidth(1.0), height: .estimated(120), scrollingDirection: .horizontal)
            .setDecoration(with: backgroundInsets)
            .setSection(with: .none, contentInsets: contentInsets)
            .setHeader(width: .fractionalWidth(1.0), height: .absolute(16))
            .setFooter(width: .fractionalWidth(0.5), height: .absolute(16))
            .build()
        
        return layoutRegister(layout)
    }
    
    /// 混合式的Layout
    /// - Returns: UICollectionViewCompositionalLayout?
    private func complexGroupLayout() -> UICollectionViewCompositionalLayout? {
        
        let groupSetting = WWCompositionalLayout.GroupSetting(width: .estimated(100), height: .absolute(200), interItemSpacing: .fixed(2), scrollingDirection: .vertical)
        let sectionSetting = WWCompositionalLayout.SectionSetting(scrollingBehavior: .continuous, contentInsets: .zero)
        
        let groupLayout1 = WWCompositionalLayout.shared
            .addItem(width: .absolute(120), height: .absolute(120), contentInsets: edgeInsets, badgeSetting: firstBadgeSetting)
            .setGroup(width: .absolute(120), height: .absolute(120), scrollingDirection: .horizontal)
            .groupLayoutMaker()
        
        let groupLayout2 = WWCompositionalLayout.shared
            .addItem(width: .absolute(60), height: .absolute(60), contentInsets: edgeInsets, badgeSetting: nil)
            .setGroup(width: .absolute(120), height: .absolute(60), scrollingDirection: .horizontal)
            .groupLayoutMaker()
        
        guard let groupLayout1 = groupLayout1,
              let groupLayout2 = groupLayout2
        else {
            return nil
        }
        
        let layout = WWCompositionalLayout.shared
            .addGroup(with: groupLayout1)
            .addGroup(with: groupLayout2)
            .setDecoration(with: backgroundInsets)
            .setSection(with: .none, contentInsets: contentInsets)
            .setHeader(width: .fractionalWidth(1.0), height: .absolute(16))
            .setFooter(width: .fractionalWidth(1.0), height: .absolute(16))
            .build(with: groupSetting, sectionSetting: sectionSetting)
        
        return layoutRegister(layout)
    }
}

// MARK: - CompositionalLayout
extension ViewController {
    
    /// 註冊CollectionReusableView
    /// - Parameter layout:
    /// - Returns: UICollectionViewLayout?
    private func layoutRegister(_ layout: UICollectionViewCompositionalLayout?) -> UICollectionViewCompositionalLayout? {
        
        guard let layout = layout else { return nil }
        
        let newLayout = layout
            ._register(with: myCollectionView, supplementaryViewClass: MyCollectionReusableHeader.self, ofKind: .header)
            ._register(with: myCollectionView, supplementaryViewClass: MyCollectionReusableHeader.self, ofKind: .footer)
            ._register(with: myCollectionView, supplementaryViewClass: MyCollectionReusableBadge.self, ofKind: .badge(key: badgeViewKey))
            ._register(with: MyCollectionReusableDecoration.self, ofKind: .decoration)
            ._register(with: MyCollectionReusableBadge.self, ofKind: .badge(key: badgeViewKey))
        
        return newLayout
    }
}
You might also like...
A tool that generate code for Swift projects, designed to improve the maintainability of UIColors
A tool that generate code for Swift projects, designed to improve the maintainability of UIColors

SwiftColorGen A tool that generate code for Swift projects, designed to improve the maintainability of UIColors. Please notice, this tool still under

Semi-automatic installation of mods for the iOS version of KOTOR 1

KOTOR 1 Mod Manager Welcome to KOTOR 1 Mod Manager (K1MM for short), a tool designed to allow easy and simple installation of mods for the iOS version

The ultimate API for iOS & OS X Auto Layout — impressively simple, immensely powerful. Objective-C and Swift compatible.
The ultimate API for iOS & OS X Auto Layout — impressively simple, immensely powerful. Objective-C and Swift compatible.

The ultimate API for iOS & OS X Auto Layout — impressively simple, immensely powerful. PureLayout extends UIView/NSView, NSArray, and NSLayoutConstrai

MyLayout is a simple and easy objective-c framework for iOS view layout
MyLayout is a simple and easy objective-c framework for iOS view layout

MyLayout is a powerful iOS UI framework implemented by Objective-C. It integrates the functions with Android Layout,iOS AutoLayout,SizeClass, HTML CSS float and flexbox and bootstrap. So you can use LinearLayout,RelativeLayout,FrameLayout,TableLayout,FlowLayout,FloatLayout,PathLayout,GridLayout,LayoutSizeClass to build your App 自动布局 UIView UITableView UICollectionView RTL

A set of libraries used for parsing representations of Swift Packages similar to how SwiftPM itself works

A set of libraries used for parsing representations of Swift Packages similar to how SwiftPM itself works, but also supporting Xcode specific features (such as Swift Playground Apps).

Fancy Swift implementation of the Visual Format Language (experimental and doesn't work with the recent version of Swift)
Fancy Swift implementation of the Visual Format Language (experimental and doesn't work with the recent version of Swift)

VFLToolbox Autolayout is awesome! VFL a.k.a Visual Format Language is even more awesome because it allows you to shorten constraints setting code. The

Simple Catalyst example (Mac idiom) of a grid-based app populated with photos, with dynamic cell layout switching
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

Declarative Auto Layout in Swift, clean and simple

Tails Tails is a take on declarative Auto Layout. If you don't like typing (like me), it might be your kind of thing! Tails is written in Swift and cu

Very simple swipe-to-dismiss, supporting Auto Layout and dynamic heights
Very simple swipe-to-dismiss, supporting Auto Layout and dynamic heights

PanelPresenter Add swipe-dismiss logic to your view controller, supporting Auto Layout and dynamic heights. Installation Add this package to your proj

Owner
William-Weng
William-Weng
IOS-IntegratedPhotoCapture - Integrated photo capture framework use podspec

IOS-IntegratedPhotoCapture integrated photo capture framework use podspec ======

Nguyễn Thành Nam 1 Dec 30, 2021
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
CompositionalLayoutDSL, library to simplify the creation of UICollectionViewCompositionalLayout. It wraps the UIKit API and makes the code shorter and easier to read.

CompositionalLayoutDSL CompositionalLayoutDSL is a Swift library. It makes easier to create compositional layout for collection view. Requirements Doc

FABERNOVEL 44 Dec 27, 2022
A project for studying of UICollectionViewCompositionalLayout

UICollectionViewCompositionalLayout A project for studying of UICollectionViewCo

donggyu 7 Jul 3, 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
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
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
Written in pure Swift, QuickLayout offers a simple and easy way to manage Auto Layout in code.

QuickLayout QuickLayout offers an additional way, to easily manage the Auto Layout using only code. You can harness the power of QuickLayout to align

Daniel Huri 243 Oct 28, 2022
Easiest way to load view classes into another XIB or storyboard.

LoadableViews Easiest way to load view classes into another XIB or storyboard. Basic setup Subclass your view from LoadableView Create a xib file, set

MLSDev 43 Nov 4, 2022
Build 1 scene, let AutoLayoutMagic generate the constraints for you!

Auto Layout Magic Create 1 scene, let Auto Layout Magic generate the constraints for you Hello friends, We've all been there. You have an app supporti

Matt 61 Sep 21, 2019