2
votes

I made a UI for displaying interests in a tag format using UICollectionView. Users select interests from a list and they get displayed as a UICollectionViewCell. Each cell contains a UILabel with the interest next to a UIButton with an "X" to delete the interest. It works mostly fine, but sometimes when adding or removing interests some of them layout improperly, as I highlighted in the image below:

Screenshot

When I print the height and width of the UILabel it shows it has the proper size (proper width shown by the green lines), but it's rendered as if I'd called sizeToFit() (which I do before making it larger, as the other cells show properly). This is the code I'm using to layout and size the cells:

class InterestsCollectionViewDelegate: NSObject, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate {

    var selectedInterests: InterestsList!    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

        let interestLabel = UILabel()
        interestLabel.text = selectedInterests.interests[indexPath.row]
        interestLabel.sizeToFit()
        interestLabel.frame.size.width += 32
        let xButtonWidth = 20;

        let newSize = CGSize(width: interestLabel.frame.size.width + CGFloat(xButtonWidth), height: CGFloat(26))
        print("newSize for \(String(describing: interestLabel.text)): \(newSize)")
        return newSize

    }
}

class LeftAlignedCollectionViewFlowLayout: UICollectionViewFlowLayout {

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        let attributes = super.layoutAttributesForElements(in: rect)

        var leftMargin = sectionInset.left
        var maxY: CGFloat = -1.0
        attributes?.forEach { layoutAttribute in
            if layoutAttribute.frame.origin.y >= maxY {
                leftMargin = sectionInset.left
            }

            layoutAttribute.frame.origin.x = leftMargin

            leftMargin += layoutAttribute.frame.width + minimumInteritemSpacing
            maxY = max(layoutAttribute.frame.maxY , maxY)
        }

        return attributes
    }


}

Any ideas of what could be causing this? Thanks in advance.

EDIT: Adding autolayout warnings from console:

2018-03-28 06:43:44.474788-0700 ClikApp[6091:3425572] [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. 
(
    "<NSLayoutConstraint:0x1c0095c70 V:|-(8)-[UICollectionView:0x13c012400]   (active, names: EditProfileContentView:0x13bd5b420, '|':EditProfileContentView:0x13bd5b420 )>",
    "<NSLayoutConstraint:0x1c0096300 V:|-(0)-[EditProfileContentView]   (active, names: EditProfileContentView:0x13bd5b420, EditProfileScrollView:0x13c08b000, '|':EditProfileScrollView:0x13c08b000 )>",
    "<NSLayoutConstraint:0x1c00968f0 V:|-(0)-[EditProfileScrollView]   (active, names: EditProfileScrollView:0x13c08b000, EditProfileSuperview:0x13bd144e0, '|':EditProfileSuperview:0x13bd144e0 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x1c0097de0 h=--- v=--- 'UIView-Encapsulated-Layout-Top' EditProfileSuperview.minY == 0   (active, names: EditProfileSuperview:0x13bd144e0 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x1c0095c70 V:|-(8)-[UICollectionView:0x13c012400]   (active, names: EditProfileContentView:0x13bd5b420, '|':EditProfileContentView:0x13bd5b420 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2018-03-28 06:43:44.497594-0700 ClikApp[6091:3425572] [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. 
(
    "<NSLayoutConstraint:0x1c0095c70 V:|-(8)-[UICollectionView:0x13c012400]   (active, names: EditProfileContentView:0x13bd5b420, '|':EditProfileContentView:0x13bd5b420 )>",
    "<NSLayoutConstraint:0x1c0096300 V:|-(0)-[EditProfileContentView]   (active, names: EditProfileContentView:0x13bd5b420, EditProfileScrollView:0x13c08b000, '|':EditProfileScrollView:0x13c08b000 )>",
    "<NSLayoutConstraint:0x1c00968f0 V:|-(0)-[EditProfileScrollView]   (active, names: EditProfileScrollView:0x13c08b000, EditProfileSuperview:0x13bd144e0, '|':EditProfileSuperview:0x13bd144e0 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x1c0097de0 h=--- v=--- 'UIView-Encapsulated-Layout-Top' EditProfileSuperview.minY == 0   (active, names: EditProfileSuperview:0x13bd144e0 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x1c0095c70 V:|-(8)-[UICollectionView:0x13c012400]   (active, names: EditProfileContentView:0x13bd5b420, '|':EditProfileContentView:0x13bd5b420 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2018-03-28 06:43:44.519629-0700 ClikApp[6091:3425572] [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. 
(
    "<NSLayoutConstraint:0x1c0095c70 V:|-(8)-[UICollectionView:0x13c012400]   (active, names: EditProfileContentView:0x13bd5b420, '|':EditProfileContentView:0x13bd5b420 )>",
    "<NSLayoutConstraint:0x1c0096300 V:|-(0)-[EditProfileContentView]   (active, names: EditProfileContentView:0x13bd5b420, EditProfileScrollView:0x13c08b000, '|':EditProfileScrollView:0x13c08b000 )>",
    "<NSLayoutConstraint:0x1c00968f0 V:|-(0)-[EditProfileScrollView]   (active, names: EditProfileScrollView:0x13c08b000, EditProfileSuperview:0x13bd144e0, '|':EditProfileSuperview:0x13bd144e0 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x1c0097de0 h=--- v=--- 'UIView-Encapsulated-Layout-Top' EditProfileSuperview.minY == 0   (active, names: EditProfileSuperview:0x13bd144e0 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x1c0095c70 V:|-(8)-[UICollectionView:0x13c012400]   (active, names: EditProfileContentView:0x13bd5b420, '|':EditProfileContentView:0x13bd5b420 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2018-03-28 06:43:44.535276-0700 ClikApp[6091:3425572] [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. 
(
    "<NSLayoutConstraint:0x1c0095c70 V:|-(8)-[UICollectionView:0x13c012400]   (active, names: EditProfileContentView:0x13bd5b420, '|':EditProfileContentView:0x13bd5b420 )>",
    "<NSLayoutConstraint:0x1c0096300 V:|-(0)-[EditProfileContentView]   (active, names: EditProfileContentView:0x13bd5b420, EditProfileScrollView:0x13c08b000, '|':EditProfileScrollView:0x13c08b000 )>",
    "<NSLayoutConstraint:0x1c00968f0 V:|-(0)-[EditProfileScrollView]   (active, names: EditProfileScrollView:0x13c08b000, EditProfileSuperview:0x13bd144e0, '|':EditProfileSuperview:0x13bd144e0 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x1c0097de0 h=--- v=--- 'UIView-Encapsulated-Layout-Top' EditProfileSuperview.minY == 0   (active, names: EditProfileSuperview:0x13bd144e0 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x1c0095c70 V:|-(8)-[UICollectionView:0x13c012400]   (active, names: EditProfileContentView:0x13bd5b420, '|':EditProfileContentView:0x13bd5b420 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2018-03-28 06:43:44.550334-0700 ClikApp[6091:3425572] [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. 
(
    "<NSLayoutConstraint:0x1c0095c70 V:|-(8)-[UICollectionView:0x13c012400]   (active, names: EditProfileContentView:0x13bd5b420, '|':EditProfileContentView:0x13bd5b420 )>",
    "<NSLayoutConstraint:0x1c0096300 V:|-(0)-[EditProfileContentView]   (active, names: EditProfileContentView:0x13bd5b420, EditProfileScrollView:0x13c08b000, '|':EditProfileScrollView:0x13c08b000 )>",
    "<NSLayoutConstraint:0x1c00968f0 V:|-(0)-[EditProfileScrollView]   (active, names: EditProfileScrollView:0x13c08b000, EditProfileSuperview:0x13bd144e0, '|':EditProfileSuperview:0x13bd144e0 )>",
    "<NSAutoresizingMaskLayoutConstraint:0x1c0097de0 h=--- v=--- 'UIView-Encapsulated-Layout-Top' EditProfileSuperview.minY == 0   (active, names: EditProfileSuperview:0x13bd144e0 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x1c0095c70 V:|-(8)-[UICollectionView:0x13c012400]   (active, names: EditProfileContentView:0x13bd5b420, '|':EditProfileContentView:0x13bd5b420 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

A few more screenshots: Screenshot2Screenshot3Screenshot4

1
Are u setting constraints in storyboard? - Shivam Tripathi
Yes, the label and button are constrained to the edges of the cell, and label.trailingedge == button.leadingedge, with the button having a fixed width. - crperez
It is hard to say what exactly causes this, but maybe ask what is different in the corrupted one? I think it is different by being the third one in its row, try creating another situation where you have 3 in a row and see if it gets corrupted also. This might help you narrow down and find the issue - Eyzuky
Thanks @Eyzuky. I haven't been able to find a pattern, sometimes it happens when I add items, sometimes when I remove items, and happens regardless of the order or line number. I am trying to fix some seemingly unrelated autolayout warnings on the console (trying to learn the Visual Format Language now). Does this look like an autolayout issue to you or something else? - crperez
Could you post 2 - 3 more screenshots? And also you can add the warnings you get in the console - Eyzuky

1 Answers

1
votes

I figured it out, I was calling sizeToFit() on the UILabel inside an update function which was called inside the cellForItemAt function. Interestingly, looks like cellForItemAt sometimes runs before sizeForItemAt, and sometimes after.

Thanks for the responses!