// // SelfSizingCollectionView.swift // VDS // // Created by Matt Bruce on 11/18/22. // import Foundation import UIKit @objc(VDSSelfSizingCollectionView) /// UICollectionView subclassed used to deal with Changing the size of itself based on its children and layout and changes of its contentSize public final class SelfSizingCollectionView: UICollectionView { private var contentSizeObservation: NSKeyValueObservation? //-------------------------------------------------- // MARK: - Lifecycle //-------------------------------------------------- /// Initializer /// - Parameters: /// - frame: Frame needed /// - layout: Layout used for this CollectionView 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) } /// Overridden to deal with Appearance Changes 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() } } //-------------------------------------------------- // 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 { /// Used within SelfSizingCollectionView to determine if there is an appearance change /// - Parameter traitCollection: TraitCollection to compare /// - Returns: True/False based on the trailCollection passed in public func hasDifferentTextAppearance(comparedTo traitCollection: UITraitCollection?) -> Bool { var result = self.preferredContentSizeCategory != traitCollection?.preferredContentSizeCategory result = result || self.legibilityWeight != traitCollection?.legibilityWeight return result } }