Search code examples
swiftconstraintsuicollectionviewcell

Strange behaviour on adding constraints based upon condition


I am making a view for conversation between two people. I have used UICollectionView with custom cell and using a flag based upon which I decide whether the message should be aligned towards left or right. The constraints work correctly if I use only a single direction of alignment, it even works correctly with condition based alignment unless number of messages in UICollectionView is less than four. So I know that constraints are not wrong. But as soon as I add the fourth message the alignment and sequence of previous cell contents and cells gets disturbed. Attached screenshot shows the problem.

correct behaviour with 3 or less messages

wrong behaviour when 4 or more messages added

var avatarHorizontal = NSLayoutConstraint(item: messageCell.avatarImageView,
                                                      attribute: .leading,
                                                      relatedBy: .equal,
                                                      toItem: cell,
                                                      attribute: .leading,
                                                      multiplier: 1,
                                                      constant: 7)
var messageTextContainerHorizontal = NSLayoutConstraint(item: messageCell.messageTextContainer,
                                                                    attribute: .leading,
                                                                    relatedBy: .equal,
                                                                    toItem: messageCell.avatarImageView,
                                                                    attribute: .trailing,
                                                                    multiplier: 1,
                                                                    constant: 10)

if isCellFromUser {
    avatarHorizontal = NSLayoutConstraint(item: messageCell.avatarImageView,
                                                       attribute: .trailing,
                                                       relatedBy: .equal,
                                                       toItem: cell,
                                                       attribute: .trailing,
                                                       multiplier: 1,
                                                       constant: -16)
    messageTextContainerHorizontal = NSLayoutConstraint(item: messageCell.messageTextContainer,
                                                                     attribute: .trailing,
                                                                     relatedBy: .equal,
                                                                     toItem: messageCell.avatarImageView,
                                                                     attribute: .leading,
                                                                     multiplier: 1,
                                                                     constant: -10)
}

UPDATE 1: I have tried @Sh_Khan's suggestion but that starts making problem from the very first cell of the collection view.

I have tried even the following code and it retains the problem in the original form I was facing previously:

if isCellFromUser {
    cell?.addConstraints([avatarRightAligned, messageTextContainerRightAligned])
} else {
    cell?.addConstraints([avatarLeftAligned, messageTextContainerLeftAligned])
}

So I have noticed a trend that as soon as I introduce the if/else statement, it makes problem no matter I reuse the reference variable for conditional assigning of constraints object or I add the constraint conditionally (having declared separate reference variables to the constraint object).

UPDATE 2: So it turned out that I had to add all the constraints in init method and not in cellForRowAt method and then activate and de-activate the relevant constraints inside cellForRowAt. Thanks to @Sh_Khan for the catch and providing the solution. Now, another problem arises, which is that I have to change background and foreground colors based on the exact same condition so what could be the solution for this one?

UPDATE 3: I have added a separate question for the problem described in UPDATE 2 here


Solution

  • It even works correctly with condition based alignment unless number of messages in UICollectionView is less than four.

    This happens as collection cells are dequeued so when the fourth cell appears it may have a left/right constraint alignment and your code tries to give it the opposite , hence a conflict

    What you can do is to create 2 constraints and set them inside init

    var leftCon,rightCon:NSLayoutConstraint!
    

    Then play with them according to message alignment inside cellForRowAt

    leftCon.isActive = isLeftAligned
    rightCon.isActive = !isLeftAligned