// // SelfSizingCollectionView.swift // VDS // // Created by Matt Bruce on 11/18/22. // import Foundation import UIKit @objc(VDSSelfSizingCollectionView) public final class SelfSizingCollectionView: UICollectionView { private var contentSizeObservation: NSKeyValueObservation? // MARK: - Lifecycle public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { super.init(frame: frame, collectionViewLayout: layout) self.setupContentSizeObservation() } public required init?(coder: NSCoder) { super.init(coder: coder) self.setupContentSizeObservation() } // MARK: - UIView public override var intrinsicContentSize: CGSize { let contentSize = self.contentSize //print(#function, contentSize) return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height) } public override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { //print(type(of: self), #function) super.traitCollectionDidChange(previousTraitCollection) // We need to handle any change that will affect layout and/or anything that affects size of a UILabel if self.traitCollection.hasDifferentTextAppearance(comparedTo: previousTraitCollection) { self.collectionViewLayout.invalidateLayout() } } public override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize { let size = super.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: horizontalFittingPriority, verticalFittingPriority: verticalFittingPriority) //print(type(of: self), #function, targetSize, "->", size) return size } // MARK: - Private private func setupContentSizeObservation() { // Observing the value of contentSize seems to be the only reliable way to get the contentSize after the collection view lays out its subviews. self.contentSizeObservation = self.observe(\.contentSize, options: [.old, .new]) { [weak self] _, change in // If we don't specify `options: [.old, .new]`, the change.oldValue and .newValue will always be `nil`. if change.newValue != change.oldValue { self?.invalidateIntrinsicContentSize() } } } } extension UITraitCollection { public func hasDifferentTextAppearance(comparedTo traitCollection: UITraitCollection?) -> Bool { var result = self.preferredContentSizeCategory != traitCollection?.preferredContentSizeCategory result = result || self.legibilityWeight != traitCollection?.legibilityWeight return result } }