8.2 C
New York
Saturday, March 1, 2025

ios – UICollectionViewCompositionalLayout masonry like with totally different row and column spans for swift


You must use UI​Assortment​View​Compositional​Format for complicated layouts like this: this is a information.

I used to be capable of provide you with this end result:

Masonry layout

MasonryLayoutViewController.swift:

import UIKit

class MasonryLayoutViewController: UIViewController {
    
    non-public let gadgets = Array(1...9)
    non-public var collectionView: UICollectionView!
    
    override func viewDidLoad() {
        tremendous.viewDidLoad()
        
        view.backgroundColor = .systemBackground
        title = "Masonry Format"
        
        collectionView = UICollectionView(
            body: .zero,
            collectionViewLayout: createMasonryLayout()
        )
        collectionView.backgroundColor = .systemBackground
        
        collectionView.register(MasonryCell.self, forCellWithReuseIdentifier: MasonryCell.reuseIdentifier)
        collectionView.dataSource = self
        
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(collectionView)
        
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
        ])
    }
    
    // MARK: - structure
    
    non-public func createMasonryLayout() -> UICollectionViewLayout {
        UICollectionViewCompositionalLayout { [weak self] (sectionIndex, layoutEnvironment) -> NSCollectionLayoutSection? in
            guard let self = self else { return nil }
            
            // variety of teams (every group has 3 gadgets)
            let groupCount = Int(ceil(Double(self.gadgets.rely) / 3.0))
            
            var teams: [NSCollectionLayoutGroup] = []
            // alternate teams with totally different layouts
            for i in 0.. NSCollectionLayoutGroup {
        // huge merchandise occupies full width of group
        let bigItemSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .fractionalHeight(1.0)
        )
        let bigItem = NSCollectionLayoutItem(layoutSize: bigItemSize)
        
        let bigGroupSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(2.0/3.0),
            heightDimension: .fractionalHeight(1.0)
        )
        let bigGroup = NSCollectionLayoutGroup.vertical(
            layoutSize: bigGroupSize,
            subitems: [bigItem]
        )
        
        let smallItemSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .fractionalHeight(0.5)
        )
        let smallItem = NSCollectionLayoutItem(layoutSize: smallItemSize)
        
        // group for 2 small gadgets, width is 1/3 of container
        let smallGroupSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0 / 3),
            heightDimension: .fractionalHeight(1.0)
        )
        let smallGroup = NSCollectionLayoutGroup.vertical(
            layoutSize: smallGroupSize,
            subitems: [smallItem, smallItem]
        )
        
        // container group with horizontal structure
        let containerSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .absolute(300)
        )
        let containerGroup = NSCollectionLayoutGroup.horizontal(
            layoutSize: containerSize,
            subitems: [bigGroup, smallGroup]
        )
        return containerGroup
    }
    
    // group the place huge merchandise is on proper and small gadgets on left
    non-public func makeBigRightGroup() -> NSCollectionLayoutGroup {
        // small gadgets, every takes half peak
        let smallItemSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .fractionalHeight(0.5)
        )
        let smallItem = NSCollectionLayoutItem(layoutSize: smallItemSize)
        
        // group for 2 small gadgets, width is 1/3 of container
        let smallGroupSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1 / 3),
            heightDimension: .fractionalHeight(1)
        )
        let smallGroup = NSCollectionLayoutGroup.vertical(
            layoutSize: smallGroupSize,
            subitems: [smallItem, smallItem]
        )
        
        // huge merchandise occupies full width of group
        let bigItemSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1),
            heightDimension: .fractionalHeight(1)
        )
        let bigItem = NSCollectionLayoutItem(layoutSize: bigItemSize)
        
        // group for large merchandise, width is 2/3 of container
        let bigGroupSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(2/3),
            heightDimension: .fractionalHeight(1)
        )
        let bigGroup = NSCollectionLayoutGroup.vertical(
            layoutSize: bigGroupSize,
            subitems: [bigItem]
        )
        
        // container group with horizontal structure (small gadgets first, then huge merchandise)
        let containerSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(1.0),
            heightDimension: .absolute(300)
        )
        let containerGroup = NSCollectionLayoutGroup.horizontal(
            layoutSize: containerSize,
            subitems: [smallGroup, bigGroup]
        )
        return containerGroup
    }
}

// MARK: - UICollectionViewDataSource

extension MasonryLayoutViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection part: Int) -> Int {
        return gadgets.rely
    }
    
    func collectionView(_ collectionView: UICollectionView,
                        cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(
            withReuseIdentifier: MasonryCell.reuseIdentifier,
            for: indexPath
        ) as! MasonryCell
        cell.configure(with: gadgets[indexPath.item])
        return cell
    }
}

MasonryCell.swift:

import UIKit

class MasonryCell: UICollectionViewCell {
    
    static let reuseIdentifier = "MasonryCell"
    
    non-public let titleLabel: UILabel = {
        let label = UILabel()
        label.font = UIFont.boldSystemFont(ofSize: 22)
        label.textColor = .white
        label.textAlignment = .middle
        return label
    }()
    
    override init(body: CGRect) {
        tremendous.init(body: body)
        
        contentView.addSubview(titleLabel)
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            titleLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            titleLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)
        ])
        
        contentView.layer.cornerRadius = 8
        contentView.layer.masksToBounds = true
        
        contentView.backgroundColor = randomColor()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been applied")
    }
    
    func configure(with quantity: Int) {
        titleLabel.textual content = "Factor (quantity)"
    }
    
    non-public func randomColor() -> UIColor {
        let r = CGFloat.random(in: 0...1)
        let g = CGFloat.random(in: 0...1)
        let b = CGFloat.random(in: 0...1)
        return UIColor(crimson: r, inexperienced: g, blue: b, alpha: 1)
    }
}

A brief clarification:

  • Gadgets:
    Every cell is represented by an NSCollectionLayoutItem. In our structure, these things are the person cells you see.

  • Teams:
    Gadgets are mixed into teams. In our case, every “container group” is a horizontal group with a hard and fast peak (300 factors) that holds 3 gadgets organized in certainly one of two configurations:
    In a “huge left” group, one huge merchandise (occupying 2/3 of the width) is positioned on the left, and a vertical subgroup (with two small gadgets, every taking half the peak) is on the best.
    In a “huge proper” group, the structure is reversed: a vertical subgroup of two small gadgets (1/3 width) is on the left, and one huge merchandise (2/3 width) is on the best.
    Every of those container teams is constructed utilizing NSCollectionLayoutGroup (and might have nested teams for the small gadgets).

  • Sections:
    All container teams are then organized in a vertical outer group (one other NSCollectionLayoutGroup) that varieties the part. This outer group is created with a calculated peak based mostly on the variety of container teams plus spacing. Lastly, the part (an NSCollectionLayoutSection) is returned by the structure supplier of the UICollectionViewCompositionalLayout.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles