Saturday, May 2, 2026
HomeiOS DevelopmentFinal UICollectionView information with iOS examples written in Swift

Final UICollectionView information with iOS examples written in Swift

[ad_1]

Learn to use UICollectionView, with extremely reusable UIKit parts and a few MVVM sample with out the going nuts with index path calculations.

UIKit

Anatomy of the UICollectionView class

In the event you’re not conversant in UICollectionView, I would counsel to get conversant in this class instantly. They’re the fundamental constructing blocks for a lot of apps supplied by Apple and different third get together builders. It is like UITableView on steroids. Here’s a fast intro about find out how to work with them by way of IB and Swift code. 💻


You may need seen that I’ve a love for steel music. On this tutorial we’ll construct an Apple Music catalog like look from floor zero utilizing solely the mighty UICollectionView class. Headers, horizontal and vertical scrolling, round photos, so principally virtually the whole lot that you will ever must construct nice person interfaces. 🤘🏻



The right way to make a UICollectionView utilizing Interface Builder (IB) in Xcode?

The quick & trustworthy reply: you should not use IB!

In the event you nonetheless need to use IB, here’s a actual fast tutorial for completely learners:


The primary steps of making your first UICollectionView based mostly display screen are these:

  • Drag a UICollectionView object to your view controller
  • Set correct constraints on the gathering view
  • Set dataSource & delegate of the gathering view
  • Prototype your cell format contained in the controller
  • Add constraints to your views contained in the cell
  • Set prototype cell class & reuse identifier
  • Perform a little coding:


import UIKit

class MyCell: UICollectionViewCell {
    @IBOutlet weak var textLabel: UILabel!
}

class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!

    override func viewDidLayoutSubviews() {
        tremendous.viewDidLayoutSubviews()

        if let flowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
            flowLayout.itemSize = CGSize(width: self.collectionView.bounds.width, peak: 120)
        }
    }
}

extension ViewController: UICollectionViewDataSource {

    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection part: Int) -> Int {
        return 10
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! MyCell
        cell.textLabel.textual content = String(indexPath.row + 1)
        return cell
    }
}

extension ViewController: UICollectionViewDelegate {

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print(indexPath.merchandise + 1)
    }
}


In a nuthsell, the information supply will present all of the required knowledge about find out how to populate the gathering view, and the delegate will deal with person occasions, resembling tapping on a cell. It’s best to have a transparent understanding concerning the knowledge supply and delegate strategies, so be at liberty to play with them for a short while. ⌨️



The right way to setup a UICollectionView based mostly display screen programmatically in Swift 5?

As you may need seen cells are the core parts of a set view. They’re derived from reusable views, which means that you probably have a listing of 1000 components, there will not be a thousand cells created for each factor, however only some that fills the scale of the display screen and if you scroll down the record this stuff are going to be reused to show your components. That is solely due to reminiscence issues, so not like UIScrollView the UICollectionView (and UITableView) class is a very sensible and efficent one, however that is additionally the explanation why you must put together (reset the contents of) the cell each time earlier than you show your precise knowledge. 😉

Initialization can be dealt with by the system, but it surely’s price to say that in case you are working with Interface Builder, it’s best to do your customization contained in the awakeFromNib technique, however in case you are utilizing code, init(body:) is your house.


import UIKit

class MyCell: UICollectionViewCell {

    weak var textLabel: UILabel!

    override init(body: CGRect) {
        tremendous.init(body: body)

        let textLabel = UILabel(body: .zero)
        textLabel.translatesAutoresizingMaskIntoConstraints = false
        self.contentView.addSubview(textLabel)
        NSLayoutConstraint.activate([
            textLabel.topAnchor.constraint(equalTo: self.contentView.topAnchor),
            textLabel.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor),
            textLabel.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor),
            textLabel.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor),
        ])
        self.textLabel = textLabel

        self.contentView.backgroundColor = .lightGray
        self.textLabel.textAlignment = .middle
    }

    required init?(coder aDecoder: NSCoder) {
        tremendous.init(coder: aDecoder)

        fatalError("Interface Builder is just not supported!")
    }

    override func awakeFromNib() {
        tremendous.awakeFromNib()

        fatalError("Interface Builder is just not supported!")
    }

    override func prepareForReuse() {
        tremendous.prepareForReuse()

        self.textLabel.textual content = nil
    }
}


Subsequent we now have to implement the view controller which is liable for managing the gathering view, we’re not utilizing IB so we now have to create it manually by utilizing Auto Structure anchors – like for the textLabel within the cell – contained in the loadView technique. After the view hierarchy is able to rock, we additionally set the information supply and delegate plus register our cell class for additional reuse. Be aware that that is achieved robotically by the system in case you are utilizing IB, however in case you favor code you must do it by calling the right registration technique. You possibly can register each nibs and lessons.


import UIKit

class ViewController: UIViewController {

    weak var collectionView: UICollectionView!

    override func loadView() {
        tremendous.loadView()

        let collectionView = UICollectionView(body: .zero, collectionViewLayout: UICollectionViewFlowLayout())
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(collectionView)
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: self.view.topAnchor),
            collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
            collectionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
        ])
        self.collectionView = collectionView
    }

    override func viewDidLoad() {
        tremendous.viewDidLoad()

        self.collectionView.backgroundColor = .white
        self.collectionView.dataSource = self
        self.collectionView.delegate = self

        self.collectionView.register(MyCell.self, forCellWithReuseIdentifier: "MyCell")
    }
}

extension ViewController: UICollectionViewDataSource {

    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection part: Int) -> Int {
        return 10
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! MyCell
        cell.textLabel.textual content = String(indexPath.row + 1)
        return cell
    }
}

extension ViewController: UICollectionViewDelegate {

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print(indexPath.row + 1)
    }
}

extension ViewController: UICollectionViewDelegateFlowLayout {

    func collectionView(_ collectionView: UICollectionView,
                        format collectionViewLayout: UICollectionViewLayout,
                        sizeForItemAt indexPath: IndexPath) -> CGSize {

        return CGSize(width: collectionView.bounds.measurement.width - 16, peak: 120)
    }
    func collectionView(_ collectionView: UICollectionView,
                        format collectionViewLayout: UICollectionViewLayout,
                        minimumLineSpacingForSectionAt part: Int) -> CGFloat {
        return 8
    }

    func collectionView(_ collectionView: UICollectionView,
                        format collectionViewLayout: UICollectionViewLayout,
                        minimumInteritemSpacingForSectionAt part: Int) -> CGFloat {
        return 0
    }

    func collectionView(_ collectionView: UICollectionView,
                        format collectionViewLayout: UICollectionViewLayout,
                        insetForSectionAt part: Int) -> UIEdgeInsets {
        return UIEdgeInsets.init(prime: 8, left: 8, backside: 8, proper: 8)
    }
}


This time it’s best to pay some consideration on the circulation format delegate strategies. You should use these strategies to supply metrics for the format system. The circulation format will show all of the cells based mostly on these numbers and sizes. sizeForItemAt is liable for the cell measurement, minimumInteritemSpacingForSectionAt is the horizontal padding, minimumLineSpacingForSectionAt is the vertical padding, and insetForSectionAt is for the margin of the gathering view part.



Utilizing supplementary components (part headers and footers)

So on this part I’ll each use storyboards, nibs and a few Swift code. That is my common method for a number of causes. Altought I really like making constraints from code, most individuals favor visible editors, so all of the cells are created inside nibs. Why nibs? Becuase you probably have a number of assortment views that is “virtually” the one good technique to share cells between them.

You possibly can create part footers precisely the identical manner as you do headers, in order that’s why this time I am solely going to give attention to headers, as a result of actually you solely have to alter one phrase with a view to use footers. ⚽️


You simply need to create two xib recordsdata, one for the cell and one for the header. Please observe that you possibly can use the very same assortment view cell to show content material within the part header, however it is a demo so let’s simply go along with two distinct objects. You do not even need to set the reuse identifier from IB, as a result of we now have to register our reusable views contained in the supply code, so simply set the cell class and join your retailers.

Cell and supplementary factor registration is barely totally different for nibs.


let cellNib = UINib(nibName: "Cell", bundle: nil)
self.collectionView.register(cellNib, forCellWithReuseIdentifier: "Cell")

let sectionNib = UINib(nibName: "Part", bundle: nil)
self.collectionView.register(sectionNib, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "Part")


Implementing the information supply for the part header appears to be like like this.


func collectionView(_ collectionView: UICollectionView,
                        viewForSupplementaryElementOfKind type: String,
                        at indexPath: IndexPath) -> UICollectionReusableView {

    guard type == UICollectionView.elementKindSectionHeader else {
        return UICollectionReusableView()
    }
    let view = collectionView.dequeueReusableSupplementaryView(ofKind: type, withReuseIdentifier: "Part", for: indexPath) as! Part

    view.textLabel.textual content = String(indexPath.part + 1)
    return view
}


Offering the scale for the circulation format delegate can be fairly simple, nonetheless generally I do not actually get the naming conventions by Apple. As soon as you must swap a form, and the opposite time there are precise strategies for particular varieties. 🤷‍♂️


func collectionView(_ collectionView: UICollectionView,
                    format collectionViewLayout: UICollectionViewLayout,
                    referenceSizeForHeaderInSection part: Int) -> CGSize {

    return CGSize(width: collectionView.bounds.measurement.width, peak: 64)
}


Ranging from iOS9 part headers and footers might be pinned to the highest or backside of the seen bounds of the gathering view.


if let flowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
    flowLayout.sectionHeadersPinToVisibleBounds = true
}


That is it, now you understand how to construct fundamental layouts with assortment view.

What about complicated circumstances, like utilizing a number of sorts of cells in the identical assortment view? Issues can get fairly messy with index paths, in order that’s why I re-invented one thing higher based mostly on a method find out how to construct superior person interfaces with assortment views showcased by Apple again at WWDC 2014.




My CollectionView based mostly UI framework

Now the fundamentals, so why do not we get straight to the purpose? I will present you my greatest follow of constructing nice person interfaces through the use of my MVVM structure based mostly CollectionView micro framework. By the way in which this framework is a superb match in case you are planning to assemble enter kinds or complicated lists.

CollectionView + ViewModel sample = ❤️ .

I will clarify the parts actual fast and after that you will discover ways to use them to construct up the Apple music-ish format that I used to be speaking about to start with. 🎶

Grid system

The primary drawback with assortment views is the scale calculation. It’s important to present the scale (width & peak) for every cell inside your assortment view.

  • if the whole lot has a hard and fast measurement inside your assortment view, you may simply set the scale properties on the circulation format itself
  • in case you want dynamic sizes per merchandise, you may implement the circulation format delegate aka. UICollectionViewDelegateFlowLayout (why is the delegate phrase in the midst of the title???) and return the precise sizes for the format system
  • in case you want much more management you may create a brand new format subclass derived from CollectionView(Movement)Structure and do all the scale calculations there

Thats good, however nonetheless you must mess with index paths, trait collections, frames and plenty of extra with a view to have a easy 2, 4, n column format that adapts on each machine. That is the explanation why I’ve created a very fundamental grid system for measurement calculation. With my grid class you may simply set the variety of columns and get again the scale for x quantity of columns, “identical to” in internet based mostly css grid techniques. 🕸


Cell reuse

Registering and reusing cells ought to and might be automated in a sort secure method. You simply need to use the cell, and also you should not care about reuse identifiers and cell registration in any respect. I’ve made a pair helper strategies with a view to make the progress extra nice. Reuse identifiers are derived from the title of the cell lessons, so that you dont’t have to fret about anymore. This can be a follow that many of the builders use.


View mannequin

view mannequin = cell (view) + knowledge (mannequin)

Filling up “template” cell with actual knowledge must be the duty of a view mannequin. That is the place MVVM comes into play. I’ve made a generic base view mannequin class, that it’s best to subclass. With the assistance of a protocol, you should use varied cells in a single assortment view with out going loopy of the row & part calculations and you may give attention to one easy process: connecting view with fashions. 😛


Part

part = header + footer + cells

I am making an attempt to emphasise that you do not need to mess with index paths, you simply need to put your knowledge collectively and that is it. Up to now I’ve struggled greater than sufficient with “pointless index path math”, so I’ve made the part object as a easy container to wrap headers, footers and all of the objects inside the part. The consequence? Generic knowledge supply class that can be utilized with a number of cells with none row or part index calculations. 👏👏👏


Supply

So with a view to make all of the issues I’ve talked about above work, I wanted to implement the gathering view delegate, knowledge supply, and circulation format delegate strategies. That is how my supply class was born. All the things is carried out right here, and I am utilizing sections, view fashions the grid system to construct up assortment views. However hey, sufficient from this principle, let’s examine it in follow. 👓



CollectionView framework instance utility

The right way to make a any record or grid format trouble free? Effectively, as a primary step simply add my CollectionView framework as a dependency. Don’t fret you will not remorse it, plus it helps Xcode 11 already, so you should use the Swift Package deal Supervisor, straight from the file menu to combine this bundle.

Tip: simply add the @_exported import CollectionView line within the AppDelegate file, then you definitely I haven’t got to fret about importing the framework file-by-file.


Step 1. Make the cell.

This step is equivalent with the common setup, besides that your cell need to be a subclass of my Cell class. Add your personal cell and do the whole lot as you’ll do usually.


import UIKit

class AlbumCell: Cell {

    @IBOutlet weak var textLabel: UILabel!
    @IBOutlet weak var detailTextLabel: UILabel!
    @IBOutlet weak var imageView: UIImageView!

    override func awakeFromNib() {
        tremendous.awakeFromNib()

        self.textLabel.font = UIFont.systemFont(ofSize: 12, weight: .daring)
        self.textLabel.textColor = .black

        self.detailTextLabel.font = UIFont.systemFont(ofSize: 12, weight: .daring)
        self.detailTextLabel.textColor = .darkGray

        self.imageView.layer.cornerRadius = 8
        self.imageView.layer.masksToBounds = true
    }

    override func reset() {
        tremendous.reset()

        self.textLabel.textual content = nil
        self.detailTextLabel.textual content = nil
        self.imageView.picture = nil
    }
}


Step 2. Make a mannequin

Simply decide a mannequin object. It may be something, however my method is to make a brand new struct or class with a Mannequin suffix. This fashion I do know that fashions are referencing the gathering view fashions inside my reusable parts folder.


import Basis

struct AlbumModel {
    let artist: String
    let title: String
    let picture: String
}



Step 3. Make the view mannequin.

Now as a substitute of configuring the cell contained in the delegate, or in a configure technique someplace, let’s make an actual view mannequin for the cell & the information mannequin that is going to be represented by way of the view.


import UIKit

class AlbumViewModel: ViewModel<AlbumCell, AlbumModel> {

    override func updateView() {
        self.view?.textLabel.textual content = self.mannequin.artist
        self.view?.detailTextLabel.textual content = self.mannequin.title
        self.view?.imageView.picture = UIImage(named: self.mannequin.picture)
    }

    override func measurement(grid: Grid) -> CGSize {
        if
            (self.collectionView.traitCollection.userInterfaceIdiom == .telephone &&
             self.collectionView.traitCollection.verticalSizeClass == .compact) ||
            self.collectionView?.traitCollection.userInterfaceIdiom == .pad
        {
            return grid.measurement(for: self.collectionView, ratio: 1.2, objects: grid.columns / 4, gaps: grid.columns - 1)
        }
        if grid.columns == 1 {
            return grid.measurement(for: self.collectionView, ratio: 1.1)
        }
        return grid.measurement(for: self.collectionView, ratio: 1.2, objects: grid.columns / 2, gaps: grid.columns - 1)
    }

}



Step 4. Setup your knowledge supply.

Now, use your actual knowledge and populate your assortment view utilizing the view fashions.


let grid = Grid(columns: 1, margin: UIEdgeInsets(all: 8))
self.collectionView.supply = .init(grid: grid, [
    [
        HeaderViewModel(.init(title: "Albums"))
        AlbumViewModel(self.album)
    ],
])
self.collectionView.reloadData()



Step 5. 🍺🤘🏻🎸

Congratulations you are achieved along with your first assortment view. With only a few traces of code you have got a ROCK SOLID code that may enable you out in many of the conditions! 😎

That is simply the tip of the iceberg! 🚢



Horizontal scrolling inside vertical scrolling

What if we make a cell that accommodates a set view and we use the identical technique like above? A group view containing a collectionview… UICollectionViewception!!! 😂

It is fully attainable, and very easy to do, the information that feeds the view mannequin will probably be a set view supply object, and also you’re achieved. Easy, magical and tremendous good to implement, additionally included within the instance app.


Sections with artists & round photos

A number of sections? No drawback, round photos? That is additionally a chunk of cake, in case you had learn my earlier tutorial about cirular assortment view cells, you will know find out how to do it, however please take a look at the supply code from gitlab and see it for youself in motion.


Callbacks and actions

Person occasions might be dealt with very simple, becuse view fashions can have delegates or callback blocks, it solely is dependent upon you which ones one you favor. The instance accommodates an onSelect handler, which is tremendous good and built-in to the framework. 😎


Dynamic cell sizing reimagined

I additionally had a tutorial about assortment view self sizing cell assist, however to be trustworthy I am not a giant fan of Apple’s official technique. After I’ve made the grid system and began utilizing view fashions, it was simpler to calculate cell heights on my own, with about 2 traces of additional code. I imagine that is price it, as a result of self sizing cells are a bit of buggy if it involves autorotation.


Rotation assist, adaptivity

Don’t fret about that an excessive amount of, you may merely change the grid or test trait collections contained in the view mannequin if you would like. I would say virtually the whole lot might be achieved proper out of the field. My assortment view micro framework is only a light-weight wrapper across the official assortment view APIs. That is the fantastic thing about it, be at liberty to do no matter you need and use it in a manner that YOU personally favor. 📦

Now go, seize the pattern code and take heed to some steel! 🤘🏻



What if I advised you… yet another factor: SwiftUI

These are some authentic quotes of mine again from April, 2018:

In the event you like this technique that is cool, however what if I advised you that there’s extra? Do you need to use the identical sample all over the place? I imply on iOS, tvOS, macOS and even watchOS. Executed deal! I’ve created the whole lot contained in the CoreKit framework. UITableViews, WKInterfaceTables are supported as effectively.

Effectively, I am a visionary, however SwiftUI was late 1 yr, it arrived in 2019:

I actually imagine that Apple this yr will method the following technology UIKit / AppKit / UXKit frameworks (written in Swift after all) considerably like this. I am not speaking concerning the view mannequin sample, however about the identical API on each platform pondering. Anyway, who is aware of this for sue, we’ll see… #wwdc18 🤔

If somebody from Apple reads this, please clarify me why the hell is SwiftUI nonetheless an abstraction layer above UIKit/ AppKit as a substitute of a refactored AppleKit UI framework that lastly unifies each single API? For actual, why? Nonetheless do not get it. ¯_(ツ)_/¯

Anyway, we’re moving into to the identical route guys, year-by-year I delete increasingly more self-written / “Third-party” code, so that you’re doing nice progress there! 🍎


[ad_2]

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments