banner



How To Register Multiple Cells For Use Within A Collectionveiw Swift 4

Ultimate UICollectionView guide with iOS examples written in Swift

Learn how to employ UICollectionView, with highly reusable UIKit components and some MVVM design without the going nuts with alphabetize path calculations.

UIKit

Anatomy of the UICollectionView form

If you're not familiar with UICollectionView, I'd suggest to go familiar with this form immediately. They're the basic building blocks for many apps provided by Apple and other third party developers. Information technology's like UITableView on steroids. Here is a quick intro near how to work with them through IB and Swift lawmaking. πŸ’»

You might have noticed that I have a love for metal music. In this tutorial nosotros're going to build an Apple Music catalog like look from ground nil using only the mighty UICollectionView class. Headers, horizontal and vertical scrolling, circular images, so basically almost everything that you'll ever need to build bang-up user interfaces. 🀘🏻


How to make a UICollectionView using Interface Builder (IB) in Xcode?

The short & honest answer: you shouldn't apply IB!

If you lot yet want to use IB, here is a real quick tutorial for absolutely beginners:

The main steps of creating your showtime UICollectionView based screen are these:

  • Drag a UICollectionView object to your view controller
  • Ready proper constraints on the collection view
  • Set dataSource & delegate of the drove view
  • Prototype your cell layout inside the controller
  • Add constraints to your views inside the prison cell
  • Fix prototype jail cell class & reuse identifier
  • Practice a piddling coding:
                          import              UIKit              class              MyCell:              UICollectionViewCell              {              @IBOutlet weak var              textLabel:              UILabel! }              class              ViewController:              UIViewController              {              @IBOutlet weak var              collectionView:              UICollectionView!              override func              viewDidLayoutSubviews() {              super.viewDidLayoutSubviews()              if let              flowLayout =              cocky.collectionView.collectionViewLayout              as?              UICollectionViewFlowLayout              {             flowLayout.itemSize              =              CGSize(width:              self.collectionView.bounds.width, height:              120)         }     } }              extension              ViewController:              UICollectionViewDataSource              {              func              numberOfSections(in collectionView:              UICollectionView) ->              Int              {              return              1              }              func              collectionView(_              collectionView:              UICollectionView, numberOfItemsInSection section:              Int) ->              Int              {              return              ten              }              func              collectionView(_              collectionView:              UICollectionView, cellForItemAt indexPath:              IndexPath) ->              UICollectionViewCell              {              allow              cell = collectionView.dequeueReusableCell(withReuseIdentifier:              "MyCell", for: indexPath)              as!              MyCell              prison cell.textLabel.text              =              String(indexPath.row              +              1)              return              cell     } }              extension              ViewController:              UICollectionViewDelegate              {              func              collectionView(_              collectionView:              UICollectionView, didSelectItemAt indexPath:              IndexPath) {              print(indexPath.particular              +              1)     } }          

In a nuthsell, the information source will provide all the required data nigh how to populate the collection view, and the delegate will handle user events, such as tapping on a cell. You should take a clear understanding almost the data source and delegate methods, so experience free to play with them for a little while. ⌨️


How to setup a UICollectionView based screen programmatically in Swift 5?

As you might have noticed cells are the core components of a collection view. They are derived from reusable views, this ways that if yous accept a listing of 1000 elements, there won't be a thousand cells created for every chemical element, but only a few that fills the size of the screen and when y'all scroll downwards the listing these items are going to be reused to brandish your elements. This is but because of memory considerations, so unlike UIScrollView the UICollectionView (and UITableView) class is a actually smart and efficent one, only this is too the reason why you accept to set up (reset the contents of) the cell every fourth dimension earlier you display your actual data. πŸ˜‰

Initialization is likewise handled past the system, but it'due south worth to mention that if you lot are working with Interface Builder, you should do your customization inside the awakeFromNib method, merely if you are using lawmaking, init(frame:) is your place.

                          import              UIKit              grade              MyCell:              UICollectionViewCell              {              weak var              textLabel:              UILabel!              override init(frame:              CGRect) {              super.init(frame: frame)              permit              textLabel =              UILabel(frame: .zero)         textLabel.translatesAutoresizingMaskIntoConstraints              =              simulated         self.contentView.addSubview(textLabel)              NSLayoutConstraint.activate([             textLabel.topAnchor.constraint(equalTo:              cocky.contentView.topAnchor),             textLabel.bottomAnchor.constraint(equalTo:              cocky.contentView.bottomAnchor),             textLabel.leadingAnchor.constraint(equalTo:              self.contentView.leadingAnchor),             textLabel.trailingAnchor.constraint(equalTo:              cocky.contentView.trailingAnchor),         ])              self.textLabel              = textLabel              self.contentView.backgroundColor              = .lightGray              self.textLabel.textAlignment              = .center              }              required init?(coder aDecoder:              NSCoder) {              super.init(coder: aDecoder)              fatalError("Interface Builder is non supported!")     }              override func              awakeFromNib() {              super.awakeFromNib()              fatalError("Interface Builder is not supported!")     }              override func              prepareForReuse() {              super.prepareForReuse()              self.textLabel.text              =              nil              } }          

Next we take to implement the view controller which is responsible for managing the collection view, we're not using IB and so we have to create information technology manually by using Auto Layout anchors - like for the textLabel in the jail cell - within the loadView method. After the view bureaucracy is ready to stone, we also set the data source and delegate plus register our cell course for further reuse. Note that this is done automatically by the system if y'all are using IB, simply if yous prefer code you have to do information technology by calling the proper registration method. You tin can register both nibs and classes.

                          import              UIKit              course              ViewController:              UIViewController              {              weak var              collectionView:              UICollectionView!              override func              loadView() {              super.loadView()              permit              collectionView =              UICollectionView(frame: .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),         ])              cocky.collectionView              = collectionView     }              override func              viewDidLoad() {              super.viewDidLoad()              self.collectionView.backgroundColor              = .white              self.collectionView.dataSource              =              self         self.collectionView.consul              =              self          cocky.collectionView.annals(MyCell.self, forCellWithReuseIdentifier:              "MyCell")     } }              extension              ViewController:              UICollectionViewDataSource              {              func              numberOfSections(in collectionView:              UICollectionView) ->              Int              {              return              ane              }              func              collectionView(_              collectionView:              UICollectionView, numberOfItemsInSection section:              Int) ->              Int              {              return              x              }              func              collectionView(_              collectionView:              UICollectionView, cellForItemAt indexPath:              IndexPath) ->              UICollectionViewCell              {              let              jail cell = collectionView.dequeueReusableCell(withReuseIdentifier:              "MyCell", for: indexPath)              as!              MyCell              cell.textLabel.text              =              String(indexPath.row              +              1)              return              jail cell     } }              extension              ViewController:              UICollectionViewDelegate              {              func              collectionView(_              collectionView:              UICollectionView, didSelectItemAt indexPath:              IndexPath) {              print(indexPath.row              +              1)     } }              extension              ViewController:              UICollectionViewDelegateFlowLayout              {              func              collectionView(_              collectionView:              UICollectionView,                         layout collectionViewLayout:              UICollectionViewLayout,                         sizeForItemAt indexPath:              IndexPath) ->              CGSize              {              return              CGSize(width: collectionView.bounds.size.width              -              sixteen, acme:              120)     }              func              collectionView(_              collectionView:              UICollectionView,                         layout collectionViewLayout:              UICollectionViewLayout,                         minimumLineSpacingForSectionAt department:              Int) ->              CGFloat              {              render              eight              }              func              collectionView(_              collectionView:              UICollectionView,                         layout collectionViewLayout:              UICollectionViewLayout,                         minimumInteritemSpacingForSectionAt section:              Int) ->              CGFloat              {              return              0              }              func              collectionView(_              collectionView:              UICollectionView,                         layout collectionViewLayout:              UICollectionViewLayout,                         insetForSectionAt department:              Int) ->              UIEdgeInsets              {              return              UIEdgeInsets.init(top:              eight, left:              eight, bottom:              8, correct:              8)     } }          

This fourth dimension you lot should pay some attention on the menstruation layout delegate methods. You can use these methods to provide metrics for the layout organization. The flow layout will display all the cells based on these numbers and sizes. sizeForItemAt is responsible for the cell size, minimumInteritemSpacingForSectionAt is the horizontal padding, minimumLineSpacingForSectionAt is the vertical padding, and insetForSectionAt is for the margin of the collection view section.


Using supplementary elements (section headers and footers)

And so in this section I'yard going to both apply storyboards, nibs and some Swift code. This is my usual arroyo for a few reasons. Altought I love making constraints from code, most people prefer visual editors, so all the cells are created inside nibs. Why nibs? Becuase if you have multiple drove views this is "most" the only overnice manner to share cells between them.

Yous tin create section footers exactly the same way every bit yous do headers, and so that'south why this time I'm only going to focus on headers, because literally you only accept to change ane word in order to use footers. ⚽️

You merely accept to create ii xib files, ane for the cell and one for the header. Please note that you lot could use the exact same collection view cell to display content in the department header, but this is a demo and so let'due south just go with ii distinct items. You don't even have to set the reuse identifier from IB, because we have to annals our reusable views within the source code, so just set the cell class and connect your outlets.

Jail cell and supplementary chemical element registration is slightly different for nibs.

                          let              cellNib =              UINib(nibName:              "Cell", bundle:              nil)              cocky.collectionView.annals(cellNib, forCellWithReuseIdentifier:              "Jail cell")              allow              sectionNib =              UINib(nibName:              "Section", bundle:              nil)              self.collectionView.register(sectionNib, forSupplementaryViewOfKind:              UICollectionView.elementKindSectionHeader, withReuseIdentifier:              "Department")          

Implementing the data source for the section header looks like this.

                          func              collectionView(_              collectionView:              UICollectionView,                         viewForSupplementaryElementOfKind kind:              String,                         at indexPath:              IndexPath) ->              UICollectionReusableView              {              baby-sit              kind ==              UICollectionView.elementKindSectionHeader              else              {              render              UICollectionReusableView()     }              permit              view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier:              "Section", for: indexPath)              as!              Section              view.textLabel.text              =              String(indexPath.department              +              one)              return              view }          

Providing the size for the period layout consul is also pretty straightforward, nevertheless sometimes I don't really get the naming conventions by Apple tree. Once you have to switch a kind, and the other time in that location are exact methods for specific kinds. 🀷‍♂️

                          func              collectionView(_              collectionView:              UICollectionView,                     layout collectionViewLayout:              UICollectionViewLayout,                     referenceSizeForHeaderInSection department:              Int) ->              CGSize              {              return              CGSize(width: collectionView.premises.size.width, top:              64) }          

Starting from iOS9 department headers and footers tin be pinned to the summit or bottom of the visible bounds of the collection view.

                          if permit              flowLayout =              cocky.collectionView.collectionViewLayout              as?              UICollectionViewFlowLayout              {     flowLayout.sectionHeadersPinToVisibleBounds              =              true              }          

That's it, now you know how to build basic layouts with collection view.

What about complex cases, like using multiple kinds of cells in the aforementioned collection view? Things tin get pretty messy with alphabetize paths, and then that's why I re-invented something better based on a technique how to build advanced user interfaces with collection views showcased by Apple tree back at WWDC 2014.


My CollectionView based UI framework

Now you know the basics, then why don't we become directly to the point? I'll testify you my best practise of edifice great user interfaces by using my MVVM architecture based CollectionView micro framework. Past the way this framework is a great fit if yous are planning to construct input forms or circuitous lists.

CollectionView + ViewModel pattern = ❤️ .

I'll explain the components real quick and later that yous'll larn how to use them to build up the Apple music-ish layout that I was talking about in the showtime. 🎢

Grid system

The commencement problem with collection views is the size calculation. Y'all have to provide the size (width & height) for each jail cell within your collection view.

  • if everything has a fixed size inside your collection view, you can just set up the size properties on the flow layout itself
  • if you demand dynamic sizes per particular, you lot tin implement the flow layout consul aka. UICollectionViewDelegateFlowLayout (why is the consul word in the centre of the proper name???) and return the exact sizes for the layout system
  • if you demand fifty-fifty more than control you tin can create a new layout subclass derived from CollectionView(Flow)Layout and do all the size calculations at that place

Thats expert, but even so you accept to mess with index paths, trait collections, frames and many more in order to have a uncomplicated 2, 4, northward column layout that adapts on every device. This is the reason why I've created a really basic filigree system for size calculation. With my filigree class you can just set the number of columns and get back the size for x corporeality of columns, "but like" in web based css grid systems. πŸ•Έ

Prison cell reuse

Registering and reusing cells should and can be automated in a type rubber style. You lot merely desire to use the jail cell, and y'all shouldn't intendance nigh reuse identifiers and cell registration at all. I've fabricated a couple helper methods in order to make the progress more pleasant. Reuse identifiers are derived from the proper name of the jail cell classes, then yous dont't take to worry near anymore. This is a practice that most of the developers apply.

View model

view model = cell (view) + data (model)

Filling upward "template" jail cell with existent data should be the task of a view model. This is where MVVM comes into play. I've made a generic base view model class, that yous should subclass. With the assist of a protocol, you can apply various cells in a single collection view without going crazy of the row & department calculations and you can focus on ane simple job: connecting view with models. πŸ˜›

Section

section = header + footer + cells

I'm trying to emphasize that you don't want to mess with index paths, you but want to put your information together and that's it. In the past I've struggled more than than enough with "unnecessary index path math", so I've made the section object as a simple container to wrap headers, footers and all the items inside of the section. The result? Generic data source class that tin be used with multiple cells without any row or section index calculations. πŸ‘πŸ‘πŸ‘

Source

And then in lodge to brand all the things I've mentioned higher up work, I needed to implement the drove view delegate, data source, and flow layout consul methods. That's how my source course was born. Everything is implemented here, and I'thousand using sections, view models the grid system to build upward collection views. Only hey, enough from this theory, let'southward come across it in practice. πŸ‘“


CollectionView framework example application

How to make a whatever list or filigree layout hassle free? Well, as a outset step just add my CollectionView framework equally a dependency. Don't worry y'all won't regret it, plus it supports Xcode 11 already, and then you can use the Swift Bundle Manager, straight from the file menu to integrate this packet.

Tip: just add the @_exported import CollectionView line in the AppDelegate file, then yous I don't have to worry well-nigh importing the framework file-past-file.

Step ane. Make the jail cell.

This step is identical with the regular setup, except that your jail cell accept to be a subclass of my Cell class. Add your ain prison cell and exercise everything as yous would do normally.

                          import              UIKit              grade              AlbumCell:              Cell              {              @IBOutlet weak var              textLabel:              UILabel!              @IBOutlet weak var              detailTextLabel:              UILabel!              @IBOutlet weak var              imageView:              UIImageView!              override func              awakeFromNib() {              super.awakeFromNib()              self.textLabel.font              =              UIFont.systemFont(ofSize:              12, weight: .assuming)              self.textLabel.textColor              = .blackness              self.detailTextLabel.font              =              UIFont.systemFont(ofSize:              12, weight: .bold)              cocky.detailTextLabel.textColor              = .darkGray              self.imageView.layer.cornerRadius              =              eight              self.imageView.layer.masksToBounds              =              true              }              override func              reset() {              super.reset()              self.textLabel.text              =              nil         self.detailTextLabel.text              =              zilch         self.imageView.image              =              nil              } }          

Pace 2. Make a model

Simply selection a model object. It can exist annihilation, merely my arroyo is to make a new struct or class with a Model suffix. This way I know that models are referencing the collection view models inside my reusable components folder.

                          import              Foundation              struct              AlbumModel {              allow              creative person:              Cord              permit              proper noun:              String              let              image:              String              }          

Step 3. Make the view model.

At present instead of configuring the cell inside the consul, or in a configure method somewhere, let's make a real view model for the cell & the data model that's going to be represented via the view.

                          import              UIKit              course              AlbumViewModel:              ViewModel<AlbumCell,              AlbumModel> {              override func              updateView() {              self.view?.textLabel.text              =              cocky.model.creative person              self.view?.detailTextLabel.text              =              self.model.name              self.view?.imageView.image              =              UIImage(named:              cocky.model.epitome)     }              override func              size(grid:              Grid) ->              CGSize              {              if              (self.collectionView.traitCollection.userInterfaceIdiom              == .phone              &&              self.collectionView.traitCollection.verticalSizeClass              == .compact) ||              cocky.collectionView?.traitCollection.userInterfaceIdiom              == .pad              {              return              filigree.size(for:              self.collectionView, ratio:              one.two, items: grid.columns              /              iv, gaps: grid.columns              -              ane)         }              if              grid.columns              ==              1              {              return              grid.size(for:              self.collectionView, ratio:              i.ane)         }              render              grid.size(for:              cocky.collectionView, ratio:              1.2, items: grid.columns              /              two, gaps: filigree.columns              -              1)     }  }          

Pace 4. Setup your information source.

Now, utilize your real data and populate your collection view using the view models.

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

Step 5. 🍺🀘🏻🎸

Congratulations yous're washed with your first collection view. With just a few lines of code you have a Rock SOLID code that will help you out in most of the situations! 😎

This is just the tip of the iceberg! 🚒

Horizontal scrolling inside vertical scrolling

What if we brand a cell that contains a drove view and we use the aforementioned method similar above? A drove view containing a collectionview... UICollectionorthwardViewception!!! πŸ˜‚

Information technology's completely possible, and really easy to do, the data that feeds the view model volition be a drove view source object, and you're done. Effortless, magical and super squeamish to implement, also included in the case app.

Sections with artists & circular images

Multiple sections? No problem, circular images? That's also a easy, if you had read my previous tutorial about cirular collection view cells, you lot'll know how to do it, merely please check out the source code from gitlab and come across it for youself in activeness.

Callbacks and deportment

User events can be handled very easy, becuse view models can have delegates or callback blocks, information technology only depends on you lot which one yous prefer. The example contains an onSelect handler, which is super nice and born to the framework. 😎

Dynamic cell sizing reimagined

I likewise had a tutorial about drove view cocky sizing cell support, just to be honest I'm not a big fan of Apple's official method. After I've made the grid arrangement and started using view models, it was more than like shooting fish in a barrel to calculate cell heights by myself, with virtually 2 lines of actress code. I believe that's worth it, because self sizing cells are a little buggy if it comes to autorotation.

Rotation support, adaptivity

Don't worry about that besides much, you can simply alter the filigree or bank check trait collections inside the view model if you want. I'd say almost everything can be done correct out of the box. My drove view micro framework is just a lightweight wrapper around the official collection view APIs. That's the beauty of it, experience free to exercise whatever you want and employ information technology in a way that Y'all personally prefer. πŸ“¦

Now become, grab the sample code and listen to some metal! 🀘🏻


What if I told you... 1 more affair: SwiftUI

These are some original quotes of mine back from April, 2018:

If you like this method that's absurd, but what if I told you that there is more? Do you desire to use the same blueprint everywhere? I mean on iOS, tvOS, macOS and fifty-fifty watchOS. Done deal! I've created everything within the CoreKit framework. UITableViews, WKInterfaceTables are supported equally well.

Well, I'grand a visionary, but SwiftUI was late 1 yr, it arrived in 2019:

I really believe that Apple this year will approach the side by side generation UIKit / AppKit / UXKit frameworks (written in Swift of form) somewhat like this. I'm not talking nearly the view model pattern, simply most the same API on every platform thinking. Anyway, who knows this for sue, we'll see... #wwdc18 πŸ€”

If someone from Apple reads this, delight explain me why the hell is SwiftUI still an abstraction layer above UIKit/ AppKit instead of a refactored AppleKit UI framework that finally unifies every unmarried API? For real, why? Even so don't get it. ¯_(ツ)_/¯

Anyhow, we're going in to the same direction guys, year-by-twelvemonth I delete more than and more than self-written / "3rd-party" code, so you're doing great progress there! 🍎

Share this article on Twitter.
Give thanks you. πŸ™

How To Register Multiple Cells For Use Within A Collectionveiw Swift 4,

Source: https://theswiftdev.com/ultimate-uicollectionview-guide-with-ios-examples-written-in-swift/

Posted by: altizeronow1953.blogspot.com

0 Response to "How To Register Multiple Cells For Use Within A Collectionveiw Swift 4"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel