HomeiOS DevelopmentMethods to create reusable views for contemporary assortment views?

Methods to create reusable views for contemporary assortment views?


A fast intro to fashionable assortment views utilizing compositional structure, diffable information supply and reusable view parts.

UIKit

Reusable views inside a generic cell


All of us like to create customized views for constructing numerous consumer interface components, proper? We additionally love to make use of assortment views to show information utilizing a grid or an inventory structure. Assortment view cells are customized views, however what if you would like to make use of the very same cell as a view?


Seems that you may present your personal UIContentConfiguration, identical to the built-in ones that you should use to setup cells to seem like checklist gadgets. When you check out the fashionable assortment views pattern code, which I extremely advocate, you will see how one can implement customized content material configurations so as to create your personal cell sorts. There are some things that I do not like about this method. 😕


To begin with, your view has to evolve to the UIContentView protocol, so it’s important to deal with further config associated stuff contained in the view. I desire the MVVM sample, so this feels a bit unusual. The second factor that you just want is a customized cell subclass, the place you additionally should deal with the configuration updates. What if there was another approach?


Let’s begin our setup by creating a brand new subclass for our future cell object, we’re merely going to supply the same old initialize technique that I at all times use for my subclasses. Apple usually calls this technique configure of their samples, however they’re roughly the identical. 😅



import UIKit

open class CollectionViewCell: UICollectionViewCell {
        
    @accessible(*, unavailable)
    personal override init(body: CGRect) {
        tremendous.init(body: body)
        
        self.initialize()
    }

    @accessible(*, unavailable)
    public required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder) isn not accessible")
    }
    
    open func initialize() {
        
    }

}


All proper, that is only a fundamental subclass so we do not have to take care of the init strategies anymore. Let’s create yet one more subclass primarily based on this object. The ReusableCell sort goes to be a generic sort, it will have a view property, which goes to be added as a subview to the contentView and we additionally pin the constraints to the content material view.


import UIKit

open class ReusableCell<View: UIView>: CollectionViewCell {
    
    var view: View!

    open override func initialize() {
        tremendous.initialize()

        let view = View()
        view.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(view)
        self.view = view
        
        NSLayoutConstraint.activate([
            view.topAnchor.constraint(equalTo: contentView.topAnchor),
            view.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
            view.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
            view.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
        ])
    }
}


By utilizing this reusable cell sort, it will be potential so as to add a customized view to the cell. We simply have to create a brand new customized view, however that is fairly a simple job to do. ✅


import UIKit

extension UIColor {

    static var random: UIColor {
        .init(crimson: .random(in: 0...1),
              inexperienced: .random(in: 0...1),
              blue: .random(in: 0...1),
              alpha: 1)
    }
}

class CustomView: View {

    let label = UILabel(body: .zero)

    override func initialize() {
        label.translatesAutoresizingMaskIntoConstraints = false
        label.numberOfLines = 0
        addSubview(label)
        
        
        backgroundColor = .random

        NSLayoutConstraint.activate([
            
            label.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
            label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
            label.topAnchor.constraint(equalTo: topAnchor, constant: 8),
            label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8),
        ])
    }

}


This tradition view has a label, which we will pin to the superview with some further padding. You’ll be able to retailer all of your subviews as sturdy properties, since Apple goes to deal with the deinit, despite the fact that the addSubview creates a robust reference, you do not have to fret about it anymore.


If you wish to create a cell that helps dynamic top, it’s best to merely pin the sting structure constraints, however if you would like to make use of a hard and fast top cell you possibly can add your personal top anchor constraint with a continuing worth. It’s a must to set a customized precedence for the peak constraint this manner the auto structure system will not break and it is going to have the ability to fulfill all the mandatory constraints.




Compositional structure fundamentals


The UICollectionViewCompositionalLayout class is a extremely adaptive and versatile structure instrument that you should use to construct fashionable assortment view layouts. It has three predominant parts that you may configure to show your customized consumer interface components in many various methods.


You mix the parts by build up from gadgets into a bunch, from teams into a piece, and eventually right into a full structure,
like on this instance of a fundamental checklist structure:


There are many nice sources and tutorials about this subject, so I will not get an excessive amount of into the main points now, however we will create a easy structure that may show full width (fractional structure dimension) gadgets in a full width group, by utilizing and estimated top to assist dynamic cell sizes. I suppose that is fairly a standard use-case for many people. We are able to create an extension on the UICollectionViewLayout object to instantiate a brand new checklist structure. 🙉



extension UICollectionViewLayout {
    static func createListLayout() -> UICollectionViewLayout {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(44))
        let merchandise = NSCollectionLayoutItem(layoutSize: itemSize)
      
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(44))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
        let part = NSCollectionLayoutSection(group: group)

        let structure = UICollectionViewCompositionalLayout(part: part)
        return structure
    }
}


Now it’s potential so as to add a collectionView to our view hierarchy contained in the view controller.


class ViewController: UIViewController {

    let collectionView = UICollectionView(body: .zero, collectionViewLayout: .createListLayout())

    override func loadView() {
        tremendous.loadView()

        collectionView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(collectionView)
        NSLayoutConstraint.activate([
            view.topAnchor.constraint(equalTo: collectionView.topAnchor),
            view.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor),
            view.leadingAnchor.constraint(equalTo: collectionView.leadingAnchor),
            view.trailingAnchor.constraint(equalTo: collectionView.trailingAnchor),
        ])
    }

    override func viewDidLoad() {
        tremendous.viewDidLoad()

    }
}


You may as well create your personal auto structure helper extensions, or use SnapKit to shortly setup your structure constraints. It’s comparatively straightforward to work with anchors, it’s best to learn my different tutorial about mastering auto structure anchors if you do not know a lot about them.




Cell registration and diffable information supply


Apple has a new set of APIs to register and dequeue cells for contemporary assortment views. It’s price to say that nearly all the things we speak about this tutorials is just accessible on iOS14+ so in case you are planning to assist an older model you will not have the ability to use these options.


If you wish to be taught extra concerning the subject, I might prefer to advocate an article by Donny Wals and there’s a nice, however a bit longer submit by John Sundell about fashionable assortment views. I am utilizing the identical helper extension to get a cell supplier utilizing a cell registration object, to make the method extra easy, plus we will want some random sentences, so let’s add just a few helpers. 💡


extension String {
    static func randomWord() -> String {
        (0..<Int.random(in: 1...10)).map { _ in String(format: "%c", Int.random(in: 97..<123)) }.joined(separator: "")
    }

    static func randomSentence() -> String {
        (0...50).map { _ in randomWord() }.joined(separator: " ")
    }
}


extension UICollectionView.CellRegistration {

    var cellProvider: (UICollectionView, IndexPath, Merchandise) -> Cell {
        { collectionView, indexPath, product in
            collectionView.dequeueConfiguredReusableCell(utilizing: self, for: indexPath, merchandise: product)
        }
    }
}


Now we will use the brand new UICollectionViewDiffableData class to specify our sections and gadgets inside the gathering view. You’ll be able to outline your sections as an enum, and on this case we will use a String sort as our gadgets. There’s a nice tutorial by AppCoda about diffable information sources.


Lengthy story quick, it’s best to make a brand new cell configuration the place now you should use the ReusableCell with a CustomView, then it’s potential to setup the diffable information supply with the cellProvider on the cellRegistration object. Lastly we will apply an preliminary snapshot by appending a brand new part and our gadgets to the snapshot. You’ll be able to replace the info supply with the snapshot and the good factor about is it that you may additionally animate the modifications in order for you. 😍


enum Part {
    case `default`
}

class ViewController: UIViewController {

    let collectionView = UICollectionView(body: .zero, collectionViewLayout: .createListLayout())
    var dataSource: UICollectionViewDiffableDataSource<Part, String>!
    let information: [String] = (0..<10).map { _ in String.randomSentence() }

    override func loadView() {
        tremendous.loadView()

        collectionView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(collectionView)
        NSLayoutConstraint.activate([
            view.topAnchor.constraint(equalTo: collectionView.topAnchor),
            view.bottomAnchor.constraint(equalTo: collectionView.bottomAnchor),
            view.leadingAnchor.constraint(equalTo: collectionView.leadingAnchor),
            view.trailingAnchor.constraint(equalTo: collectionView.trailingAnchor),
        ])
    }

    override func viewDidLoad() {
        tremendous.viewDidLoad()

        collectionView.delegate = self

        createDataSource()
        applyInitialSnapshot()
    }

    func createDataSource() {
        let cellRegistration = UICollectionView.CellRegistration<ReusableCell<CustomView>, String> { cell, indexPath, mannequin in
            cell.view.label.textual content = mannequin
        }

        dataSource = UICollectionViewDiffableDataSource<Part, String>(collectionView: collectionView,
                                                                         cellProvider: cellRegistration.cellProvider)
    }
    
    func applyInitialSnapshot() {
        var snapshot = NSDiffableDataSourceSnapshot<Part, String>()
        snapshot.appendSections([.default])
        snapshot.appendItems(information)
        dataSource.apply(snapshot, animatingDifferences: true)
    }
}

extension ViewController: UICollectionViewDelegate {

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        let merchandise = dataSource.itemIdentifier(for: indexPath)

        print(merchandise ?? "n/a")
    }
}


You continue to should implement a delegate technique if you would like to deal with cell choice, however fortuitously the diffable information supply has an itemIdentifier technique to lookup components inside the info supply.


As you possibly can see it is fairly straightforward to provide you with a generic cell that can be utilized to render a customized view inside a set view. I imagine that the “official” cell configuration primarily based method is a little more sophisticated, plus it’s important to write numerous code if it involves fashionable assortment views.


I will replace my unique assortment view framework with these new methods for positive. The brand new compositional structure is far more highly effective in comparison with common circulation layouts, diffable information sources are additionally wonderful and the brand new cell registration API can also be good. I imagine that the gathering view crew at Apple did a tremendous job through the years, it is nonetheless one in every of my favourite parts if it involves UIKit growth. I extremely advocate studying these fashionable methods. 👍




RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments