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.
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! π
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