Replace word: Andriy Kharchyshyn up to date this tutorial for Xcode 11, Swift 5 and iOS 13. Paride Broggi wrote the unique.
UICollectionView, launched in iOS 6, has change into one of the crucial widespread UI components amongst iOS builders. What makes it so enticing is the separation between the info and presentation layers, which is dependent upon a separate object to deal with the format. The format is then liable for figuring out the position and visible attributes of the views.
You’ve seemingly used the default stream format, a format class supplied by UIKit. It’s a primary grid format with some customizations.
However you too can implement your individual customized layouts to rearrange the views any manner you want. This makes the gathering view versatile and highly effective.
On this UICollectionView customized format tutorial, you’ll create a format impressed by the favored Pinterest app.
Within the course of, you’ll be taught:
About customized layouts.
calculate and cache format attributes.
deal with dynamically sized cells.
Observe: This tutorial requires a primary information of UICollectionView. Should you’re not conversant in it, you possibly can be taught extra in our written or video tutorial sequence:
Able to jazz up your assortment view? Learn on!
Obtain the mission supplies by clicking the Obtain Supplies button on the high or backside of this tutorial. Open the starter mission in Xcode.
Construct and run the mission. You’ll see the next:
The app presents a gallery of images from RWDevCon. You’ll be able to browse the images and see how a lot enjoyable the attendees had whereas on the convention.
The gallery makes use of a group view with an ordinary stream format. At first sight, it appears OK. However you possibly can definitely enhance the format design.
The images don’t utterly fill the content material space. Lengthy captions get truncated. The consumer expertise is boring and static as a result of all cells are the identical dimension.
You could possibly enhance the design with a customized format the place every cell is free to be the dimensions that completely suits its wants.
Creating Customized Assortment View Layouts
You’ll create a shocking assortment view by first making a customized format class on your gallery.
Assortment view layouts are subclasses of the summary class UICollectionViewLayout. They outline the visible attributes of each merchandise in your assortment view.
The person attributes are cases of UICollectionViewLayoutAttributes. These comprise the properties of every merchandise in your assortment view, such because the merchandise’s body or rework.
Create a brand new file contained in the Layouts group. Choose Cocoa Contact Class from the iOS ▸ Supply listing. Title it PinterestLayout and make it a subclass of UICollectionViewLayout.
Subsequent, configure the gathering view to make use of your new format. Open Foremost.storyboard. Choose the Assortment View within the Photograph Stream View Controller Scene as proven under:
Subsequent, open the Attributes inspector. Choose Customized within the Structure drop-down listing. Then choose PinterestLayout within the Class drop-down listing:
Okay — time to see the way it appears. Construct and run your app:
Don’t panic! Imagine it or not, this can be a good signal.
This implies the gathering view is utilizing your customized format class. The cells don’t present as a result of PinterestLayout doesn’t implement any of the strategies concerned within the format course of but.
Core Structure Course of
Take into consideration the gathering view format course of. It’s a collaboration between the gathering view and the format object. When the gathering view wants some format data, it asks your format object to supply it by calling sure strategies in a particular order:
Your format subclass should implement the next strategies:
collectionViewContentSize: This technique returns the width and peak of the gathering view’s contents. It’s essential to implement it to return the peak and width of your complete assortment view’s content material, not simply the seen content material. The gathering view makes use of this data internally to configure its scroll view’s content material dimension.
put together(): At any time when a format operation is about to happen, UIKit calls this technique. It’s your alternative to arrange and carry out any calculations required to find out the gathering view’s dimension and the positions of the gadgets.
layoutAttributesForElements(in:): On this technique, you come the format attributes for all gadgets contained in the given rectangle. You come back the attributes to the gathering view as an array of UICollectionViewLayoutAttributes.
layoutAttributesForItem(at:): This technique gives on demand format data to the gathering view. You could override it and return the format attributes for the merchandise on the requested indexPath.
Okay, so you already know what it is advisable implement. However how do you go about calculating these attributes?
Calculating Structure Attributes
For this format, it is advisable dynamically calculate the peak of each merchandise because you don’t know the photograph’s peak upfront. You’ll declare a protocol that’ll present this data when PinterestLayout wants it.
Now, again to the code. Open PinterestLayout.swift. Add the next delegate protocol declaration earlier than PinterestLayout class:
protocol PinterestLayoutDelegate: AnyObject
This code declares a PinterestLayoutDelegate protocol. It has a technique to request the photograph’s peak. You’ll implement this protocol in PhotoStreamViewController shortly.
There’s another factor to do earlier than implementing the format strategies. You could declare some properties that’ll assist with the format course of.
Add the next to PinterestLayout:
weak var delegate: PinterestLayoutDelegate?
personal let numberOfColumns = 2
personal let cellPadding: CGFloat = 6
personal var cache: [UICollectionViewLayoutAttributes] = 
personal var contentHeight: CGFloat = zero
personal var contentWidth: CGFloat
guard let collectionView = collectionView else
let insets = collectionView.contentInset
return collectionView.bounds.width – (insets.left + insets.proper)
override var collectionViewContentSize: CGSize
return CGSize(width: contentWidth, peak: contentHeight)
This code defines some properties you’ll want later to supply the format data. Right here it’s, defined step-by-step:
This retains a reference to the delegate.
These are two properties for configuring the format: The variety of columns and the cell padding.
That is an array to cache the calculated attributes. Whenever you name put together(), you’ll calculate the attributes for all gadgets and add them to the cache. When the gathering view later requests the format attributes, you possibly can effectively question the cache as an alternative of recalculating them each time.
This declares two properties to retailer the content material dimension. You increment contentHeight as you add images and calculate contentWidth primarily based on the gathering view width and its content material inset.
collectionViewContentSize returns the dimensions of the gathering view’s contents. You employ each contentWidth and contentHeight from earlier steps to calculate the dimensions.
You’re able to calculate the attributes for the gathering view gadgets. For now, it’ll include the body. To know the way you’ll do that, check out the next diagram:
You’ll calculate the body of each merchandise primarily based on its column and the place of the earlier merchandise in the identical column. You do that by monitoring xOffset for the body, and yOffset for the place of the earlier merchandise.
You’ll first use the beginning X coordinate of the column the merchandise belongs to as a way to calculate the horizontal place, after which add the cell padding. The vertical place is the beginning place of the prior merchandise in that column, plus the peak of that prior merchandise. The general merchandise peak is the sum of the picture peak and the content material padding.
You’ll do that in put together(). Your main goal is to calculate an occasion of UICollectionViewLayoutAttributes for each merchandise within the format.
Add the next technique to PinterestLayout:
override func put together()
Taking every numbered remark in flip:
You solely calculate the format attributes if cache is empty and the gathering view exists.
Declare and fill the xOffset array with the x-coordinate for each column primarily based on the column widths. The yOffset array tracks the y-position for each column. You initialize every worth in yOffset to zero, since that is the offset of the primary merchandise in every column.
Loop by means of all of the gadgets within the first part since this specific format has just one part.
Carry out the body calculation. width is the beforehand calculated cellWidth with the padding between cells eliminated. Ask the delegate for the peak of the photograph, then calculate the body peak primarily based on this peak and the predefined cellPadding for the highest and backside.
If there is no such thing as a delegate set, use default cell peak. You then mix this with the x and y offsets of the present column to create insetFrame utilized by the attribute.
Create an occasion of UICollectionViewLayoutAttributes, set its body utilizing insetFrame and append the attributes to cache.
Broaden contentHeight to account for the body of the newly calculated merchandise. Then, advance the yOffset for the present column primarily based on the body. Lastly, advance the column so the following merchandise will probably be positioned within the subsequent column.
Observe: Since put together() is named every time the gathering view’s format turns into invalid, there are a lot of conditions in a typical implementation the place you would possibly must recalculate attributes right here. For instance, the bounds of the UICollectionView would possibly change when the orientation modifications. They may additionally change if gadgets are added or faraway from the gathering.
These circumstances are out of scope for this tutorial, however it’s essential to concentrate on them in a non-trivial implementation.
Now it is advisable override layoutAttributesForElements(in:). The gathering view calls it after put together() to find out which gadgets are seen within the given rectangle.
Add the next code to the very finish of PinterestLayout:
override func layoutAttributesForElements(in rect: CGRect)
var visibleLayoutAttributes: [UICollectionViewLayoutAttributes] = 
// Loop by means of the cache and search for gadgets within the rect
for attributes in cache
Right here, you iterate by means of the attributes in cache and test if their frames intersect with rect the gathering view gives.
You add any attributes with frames that intersect with that rect to visibleLayoutAttributes, which is ultimately returned to the gathering view.
The final technique you should implement is layoutAttributesForItem(at:).
override func layoutAttributesForItem(at indexPath: IndexPath)
Right here, you retrieve and return from cache the format attributes which correspond to the requested indexPath.
Connecting with UIViewController
Earlier than you possibly can see your format in motion, it is advisable implement the format delegate. PinterestLayout depends on this to supply photograph and caption heights when calculating the peak of an merchandise’s body.
Open PhotoStreamViewController.swift. Add the next extension to the top of the file to implement PinterestLayoutDelegate:
extension PhotoStreamViewController: PinterestLayoutDelegate
Right here, you present the format with the precise peak of the images.
Subsequent, add the next code inside viewDidLoad(), proper under the decision to tremendous:
if let format = collectionView?.collectionViewLayout as? PinterestLayout
This units PhotoStreamViewController because the delegate on your format.
Time to see how issues are shaping up! Construct and run your app. You may see the cells are correctly positioned and sized primarily based on the heights of the images:
You have now constructed a very customized assortment view format. Nice work!
The place to Go From Right here?
You’ll be able to obtain the ultimate mission with the entire code from the tutorial by clicking the Obtain Supplies button on the high or backside of this tutorial.
With much less work than you in all probability imagined, you’ve got created your very personal Pinterest-like customized format!
If you would like to be taught extra about customized layouts, contemplate the next sources:
You probably have any questions or feedback on this tutorial, be at liberty to affix the dialogue under within the boards!