Search code examples
iosswiftuicollectionviewautolayoutnslayoutconstraint

Resizable/Dynamic UICollectionView width (not a cell dynamic width!!)


I have a horizontal UICollectionView with green background (attached image) and I`d like to adjust its width depending on how many cells are available. After that, the button needs to be attached to collection views trailing anchor

For example: I have one photo in collection view, and its view width is 100. User pressed button, added photo to the collection, and width, increased to 200. Therefore addImageButton has moved to the right.

It should look something like this:

resizable collection view

My current code is this:

class ImagesViewController: UIViewController {
    
    lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        layout.minimumLineSpacing = 8
        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.register(ImagesCollectionViewCell.self, forCellWithReuseIdentifier: ImagesCollectionViewCell.reuseIdentifier)
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        return collectionView
    }()
    
    lazy var addImageButton: UIButton = {
        let btn = UIButton()
        btn.layer.cornerRadius = 8
        btn.clipsToBounds = true
        btn.contentMode = .scaleAspectFill
        btn.backgroundColor = Colors.tintDefault
        btn.setImage(UIImage(named: "addImage"), for: .normal)
        btn.translatesAutoresizingMaskIntoConstraints = false
        btn.addTarget(self, action: #selector(handleAddImage), for: .touchUpInside)
        return btn
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = Colors.backgroundPrimary
        
        [collectionView, addImageButton].forEach { view.addSubview ($0) }
        
        collectionView.anchor(top: top.bottomAnchor, leading: view.leadingAnchor, paddingTop: 20, width: 272, height: 80)
        collectionView.backgroundColor = .green
        
        addImageButton.centerYAnchor.constraint(equalTo: collectionView.centerYAnchor).isActive = true
        addImageButton.anchor(leading: collectionView.trailingAnchor, paddingLeft: 32, width: 24, height: 24)
        
        
    }
}

extension ImagesViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
    // Some code here... (numberOfItemsInSection, cellForItemAt, etc)
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: 80, height: 80)
    }
    
}

Please read the question carefully!

Dynamic collectionView width is needed, not the cell width!

Please help me out to set dynamic width for collection view


Solution

  • Try following :

    1. Declare a variable inside your UIViewController subclass like this.
    private var contentSizeObservation: NSKeyValueObservation?
    
    1. Inside viewDidLoad() or whenever your myCollectionView is set up, start observing contentSize changes.
    contentSizeObservation = myCollectionView.observe(\.contentSize, options: .new, changeHandler: { [weak self] (cv, _) in
        guard let self = self else { return }
        self.myCollectionViewWidthConstraint.constant = cv.contentSize.width
    })
    
    1. Do NOT Forget to clean this up in deinit call -
    deinit {
        contentSizeObservation?.invalidate()
    }
    

    This approach will make sure that your myCollectionView is always as big in width as it's content.