I have a constraint on my `UIImageView` that is being broken, but I don't understand why. It appears that it is due to the `estimated` item size on my collection view's compositional layout. Any thoughts would be greatly appreciated.
Note: code formatting appears to be messed up after pasting, apologies for that.
Cell and Layout Section Definition
//
// PostCell.swift
//
// Created by Sam Jackson on 10/13/19.
// Copyright © 2019 Vita. All rights reserved.
//
import UIKit
import Logging
import Kingfisher
import Foundation
// MARK: PostCell
class PostCell: UICollectionViewCell {
static let reuseIdentifier = "PostCell"
static let authorPhotoSize: CGFloat = 64.0
/// The post that drives the content of this `UICollectionViewCell`. When this
/// property is set, the content of the cell will change.
var post: Post! {
didSet {
displayNameLabel.text = post.author.displayName
metadataLabel.text = "This is a test."
authorPhotoImageView.kf.setImage(with: post.author.photoURL)
}
}
/// Display name of the post author.
var displayNameLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 20, weight: .medium)
label.textColor = .label
return label
}()
/// Typically shows the timestamp for the post.
var metadataLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.systemFont(ofSize: 16, weight: .regular)
label.textColor = .gray
return label
}()
/// Profile photo of the post author.
var authorPhotoImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.cornerRadius = PostCell.authorPhotoSize / 2
imageView.clipsToBounds = true
return imageView
}()
/// This `UIStackView` organizes the labels in the post header. Specifically, it arranges
/// the `displayNameLabel` and `metadataLabel` in a vertical stack.
lazy var headerLabelStack: UIStackView = {
let stack = UIStackView(
arrangedSubviews: [displayNameLabel, metadataLabel]
)
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
return stack
}()
/// This `UIStackView` composes the different elements of the post header. Specifically,
/// it arranges the `authorPhotoImageView` and `headerLabelStack` in a horizontal stack.
lazy var headerStack: UIStackView = {
let stack = UIStackView(
arrangedSubviews: [authorPhotoImageView, headerLabelStack]
)
stack.translatesAutoresizingMaskIntoConstraints = false
stack.alignment = .center
stack.axis = .horizontal
stack.spacing = 8
return stack
}()
/// This is the all-encompassing stack for a `Post`. By default, it comprises of a header and footer.
/// It can be configured to contain different elements of a post, such as a photo or video.
lazy var postStack: UIStackView = {
let stack = UIStackView(
arrangedSubviews: [headerStack]
)
stack.translatesAutoresizingMaskIntoConstraints = false
stack.axis = .vertical
stack.spacing = 8
return stack
}()
/// Update the data presented in the cell with some `Post`.
func configure(with post: Post) {
self.post = post
}
override init(frame: CGRect) {
super.init(frame: frame)
// All other subviews are managed by stacks.
contentView.addSubview(postStack)
// Activate all necessary constraints.
NSLayoutConstraint.activate([
authorPhotoImageView.heightAnchor.constraint(equalToConstant: PostCell.authorPhotoSize),
authorPhotoImageView.widthAnchor.constraint(equalToConstant: PostCell.authorPhotoSize),
postStack.topAnchor.constraint(equalTo: contentView.topAnchor),
postStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
postStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: postStack.bottomAnchor)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: DefinesLayoutSection
extension PostCell {
/// Because all posts will be presented the same way within a hosting `UICollectionView`,
/// we statically define their layout section here.
static func getLayoutSection(withDivider: Bool = false) -> NSCollectionLayoutSection {
// Each cell should span the width of the collection view.
// The height of the cell should be dynamic.
let item = NSCollectionLayoutItem(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(44.0)
)
)
// We aren't doing anything with groups at this time, so we leave
// these settings the same as those for the `NSCollectionLayoutItem`.
let groupSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .estimated(44.0)
)
let group = NSCollectionLayoutGroup.vertical(
layoutSize: groupSize,
subitem: item,
count: 1
)
// Insets for the section should match our other sections.
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(
top: 0,
leading: 17,
bottom: 0,
trailing: 17
)
// Space between cells.
section.interGroupSpacing = 17.0
return section
}
}
Console Output
2019-10-13 21:03:41.424629-0400 [6217:1241993] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSAutoresizingMaskLayoutConstraint:0x28312fb60 h=--& v=--& UIView:0x10c7bef60.height == 44 (active)>",
"<NSLayoutConstraint:0x28312caf0 UIImageView:0x10c7beda0.height == 64 (active)>",
"<NSLayoutConstraint:0x28312cb90 V:|-(0)-[UIStackView:0x10c7bf580] (active, names: '|':UIView:0x10c7bef60 )>",
"<NSLayoutConstraint:0x28312cc80 V:[UIStackView:0x10c7bf580]-(0)-| (active, names: '|':UIView:0x10c7bef60 )>",
"<NSLayoutConstraint:0x28312f7a0 'UISV-canvas-connection' UIStackView:0x10c7bf400.top == _UILayoutSpacer:0x282d663a0'UISV-alignment-spanner'.top (active)>",
"<NSLayoutConstraint:0x28312f840 'UISV-canvas-connection' UIStackView:0x10c7bf400.centerY == UIImageView:0x10c7beda0.centerY (active)>",
"<NSLayoutConstraint:0x28312f930 'UISV-canvas-connection' UIStackView:0x10c7bf580.top == UIStackView:0x10c7bf400.top (active)>",
"<NSLayoutConstraint:0x28312f980 'UISV-canvas-connection' V:[UIStackView:0x10c7bf400]-(0)-| (active, names: '|':UIStackView:0x10c7bf580 )>",
"<NSLayoutConstraint:0x28312f610 'UISV-spanning-boundary' _UILayoutSpacer:0x282d663a0'UISV-alignment-spanner'.top <= UIImageView:0x10c7beda0.top (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x28312caf0 UIImageView:0x10c7beda0.height == 64 (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.