Skip to content

Instantly share code, notes, and snippets.

@romyios
Last active August 31, 2021 00:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save romyios/d5ce0cc2451d43827fbc37f194289a5a to your computer and use it in GitHub Desktop.
Save romyios/d5ce0cc2451d43827fbc37f194289a5a to your computer and use it in GitHub Desktop.
From apple's modernizing collection view
private class CustomListCell: ItemListCell {
private func defaultListContentConfiguration() -> UIListContentConfiguration { return .subtitleCell() }
private lazy var listContentView = UIListContentView(configuration: defaultListContentConfiguration())
private let categoryIconView = UIImageView()
private let categoryLabel = UILabel()
private var customViewConstraints: (categoryLabelLeading: NSLayoutConstraint,
categoryLabelTrailing: NSLayoutConstraint,
categoryIconTrailing: NSLayoutConstraint)?
private func setupViewsIfNeeded() {
// We only need to do anything if we haven't already setup the views and created constraints.
guard customViewConstraints == nil else { return }
contentView.addSubview(listContentView)
contentView.addSubview(categoryLabel)
contentView.addSubview(categoryIconView)
listContentView.translatesAutoresizingMaskIntoConstraints = false
let defaultHorizontalCompressionResistance = listContentView.contentCompressionResistancePriority(for: .horizontal)
listContentView.setContentCompressionResistancePriority(defaultHorizontalCompressionResistance - 1, for: .horizontal)
categoryLabel.translatesAutoresizingMaskIntoConstraints = false
categoryIconView.translatesAutoresizingMaskIntoConstraints = false
let constraints = (
categoryLabelLeading: categoryLabel.leadingAnchor.constraint(greaterThanOrEqualTo: listContentView.trailingAnchor),
categoryLabelTrailing: categoryIconView.leadingAnchor.constraint(equalTo: categoryLabel.trailingAnchor),
categoryIconTrailing: contentView.trailingAnchor.constraint(equalTo: categoryIconView.trailingAnchor)
)
NSLayoutConstraint.activate([
listContentView.topAnchor.constraint(equalTo: contentView.topAnchor),
listContentView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
listContentView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
categoryLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
categoryIconView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
constraints.categoryLabelLeading,
constraints.categoryLabelTrailing,
constraints.categoryIconTrailing
])
customViewConstraints = constraints
}
private var separatorConstraint: NSLayoutConstraint?
private func updateSeparatorConstraint() {
guard let textLayoutGuide = listContentView.textLayoutGuide else { return }
if let existingConstraint = separatorConstraint, existingConstraint.isActive {
return
}
let constraint = separatorLayoutGuide.leadingAnchor.constraint(equalTo: textLayoutGuide.leadingAnchor)
constraint.isActive = true
separatorConstraint = constraint
}
/// - Tag: UpdateConfiguration
override func updateConfiguration(using state: UICellConfigurationState) {
setupViewsIfNeeded()
// Configure the list content configuration and apply that to the list content view.
var content = defaultListContentConfiguration().updated(for: state)
content.imageProperties.preferredSymbolConfiguration = .init(font: content.textProperties.font, scale: .large)
content.image = state.item?.image
content.text = state.item?.title
content.secondaryText = state.item?.description
content.axesPreservingSuperviewLayoutMargins = []
listContentView.configuration = content
// Get the list value cell configuration for the current state, which we'll use to obtain the system default
// styling and metrics to copy to our custom views.
let valueConfiguration = UIListContentConfiguration.valueCell().updated(for: state)
// Configure custom image view for the category icon, copying some of the styling from the value cell configuration.
categoryIconView.image = state.item?.category.icon
categoryIconView.tintColor = valueConfiguration.imageProperties.resolvedTintColor(for: tintColor)
categoryIconView.preferredSymbolConfiguration = .init(font: valueConfiguration.secondaryTextProperties.font, scale: .small)
// Configure custom label for the category name, copying some of the styling from the value cell configuration.
categoryLabel.text = state.item?.category.name
categoryLabel.textColor = valueConfiguration.secondaryTextProperties.resolvedColor()
categoryLabel.font = valueConfiguration.secondaryTextProperties.font
categoryLabel.adjustsFontForContentSizeCategory = valueConfiguration.secondaryTextProperties.adjustsFontForContentSizeCategory
// Update some of the constraints for our custom views using the system default metrics from the configurations.
customViewConstraints?.categoryLabelLeading.constant = content.directionalLayoutMargins.trailing
customViewConstraints?.categoryLabelTrailing.constant = valueConfiguration.textToSecondaryTextHorizontalPadding
customViewConstraints?.categoryIconTrailing.constant = content.directionalLayoutMargins.trailing
updateSeparatorConstraint()
}
}
// This list cell subclass is an abstract class with a property that holds the item the cell is displaying,
// which is added to the cell's configuration state for subclasses to use when updating their configuration.
private class ItemListCell: UICollectionViewListCell {
private var item: Item? = nil
func updateWithItem(_ newItem: Item) {
guard item != newItem else { return }
item = newItem
setNeedsUpdateConfiguration()
}
override var configurationState: UICellConfigurationState {
var state = super.configurationState
state.item = self.item
return state
}
}
// thank you apple!
@available(iOS 6.0, *)
open class UICollectionViewCell : UICollectionReusableView {
/// Requests the cell update its configuration for its current state. This method is called automatically
/// when the cell's `configurationState` may have changed, as well as in other circumstances where an
/// update may be required. Multiple requests may be coalesced into a single update at the appropriate time.
@available(iOS 14.0, *)
open func setNeedsUpdateConfiguration()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment