How do I resize a UICollectionViewListCell containing a UITextView?

This is a simple collection view with compositional layout and diffable data source.

It displays one cell, of type UICollectionViewListCell, whose contentView has a text view as a subview.

import UIKit

class ViewController: UIViewController {
    var collectionView: UICollectionView!
    let textView = UITextView()
    
    var dataSource: UICollectionViewDiffableDataSource<Section, Int>!
    
    enum Section: CaseIterable {
        case first
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        configureHierarchy()
        configureDataSource()
    }
    
    private func configureHierarchy() {
        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: createLayout())
        view.addSubview(collectionView)
        collectionView.autoresizingMask = [.flexibleHeight, .flexibleWidth]

        textView.delegate = self
    }
    
    func configureDataSource() {
        let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Int> { [weak self] cell, indexPath, itemIdentifier in
            guard let self else { return }
            
            cell.contentView.addSubview(textView)
            textView.pinToSuperviewMargins()
        }
        
        dataSource = .init(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
            collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: itemIdentifier)
        }
        
        var snapshot = NSDiffableDataSourceSnapshot<Section, Int>()
        snapshot.appendSections(Section.allCases)
        snapshot.appendItems([1], toSection: .first)
        dataSource.apply(snapshot)
    }
    
    func createLayout() -> UICollectionViewLayout {
        UICollectionViewCompositionalLayout { section, layoutEnvironment in
            var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
            return NSCollectionLayoutSection.list(using: config, layoutEnvironment: layoutEnvironment)
        }
    }
}
extension ViewController: UITextViewDelegate {
    func textViewDidChange(_ textView: UITextView) {
        // Do something here?
    }
}

The pinToSuperviewMargins method sets the top, bottom, leading and trailing constraints of the view on which it's called to its superview's and its translatesAutoResizingMaskIntoConstraints property to false:

extension UIView {
    func pinToSuperviewMargins(
        top: CGFloat = 0,
        bottom: CGFloat = 0,
        leading: CGFloat = 0,
        trailing: CGFloat = 0,
        file: StaticString = #file,
        line: UInt = #line
    ) {
        guard let superview = self.superview else {
            let localFilePath = URL(fileURLWithPath: "\(file)").lastPathComponent
            print(">> \(#function) failed in file: \(localFilePath), at line: \(line): could not find \(Self.self).superView.")
            return
        }
        
        self.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            self.topAnchor.constraint(equalTo: superview.topAnchor, constant: top),
            self.bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: bottom),
            self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: leading),
            self.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: trailing),
        ])
    }
    
    func pinToSuperviewMargins(constant c: CGFloat = 0, file: StaticString = #file, line: UInt = #line) {
        self.pinToSuperviewMargins(top: c, bottom: c, leading: c, trailing: c, file: file, line: line)
    }
}

I tried calling collectionView.setNeedsLayout() in textViewDidChange(_:) but it doesn't work.

I used to accomplish cell resizing with tableView.beginUpdates(); tableView.endUpdates() when dealing with table views.

Accepted Reply

Set the selfSizingInvalidation property on the UICollectionView to .enabledIncludingConstraints. This will cause the collection view to automatically detect Auto Layout and intrinsic content size changes within the cell, and resize the cell when necessary.

Links to the relevant documentation pages:

https://developer.apple.com/documentation/uikit/uicollectionview/4001100-selfsizinginvalidation

https://developer.apple.com/documentation/uikit/uicollectionview/selfsizinginvalidation/enabledincludingconstraints

  • @Frameworks Engineer Thank you very much. You also need to set the text view's isScrollEnabled property to false.

Add a Comment

Replies

Set the selfSizingInvalidation property on the UICollectionView to .enabledIncludingConstraints. This will cause the collection view to automatically detect Auto Layout and intrinsic content size changes within the cell, and resize the cell when necessary.

Links to the relevant documentation pages:

https://developer.apple.com/documentation/uikit/uicollectionview/4001100-selfsizinginvalidation

https://developer.apple.com/documentation/uikit/uicollectionview/selfsizinginvalidation/enabledincludingconstraints

  • @Frameworks Engineer Thank you very much. You also need to set the text view's isScrollEnabled property to false.

Add a Comment