testing
testing

Reputation: 20279

Update constraints for supplementary view of UICollectionView on orientation change

I'm using a header (supplementary view) in my UICollectionView. On the header I have label which has a certain distance from the left side. I'm doing my calculation and setting the constant of a constraint. Everything does work as expected.

If the orientation is now changed, the labels have the old position. Now I need to update the constraints for all my headers. Calling invalidateLayout doesn't update the constraints. How can I manually trigger the recalculation?

Edit:

This is how my layoutSubviews does look like, where the recalculation takes place:

public override void LayoutSubviews ()
{
    this.width = this.collectionViewSize.Width;
    this.itemWidth  = (nfloat)Math.Round(this.width / numberOfItemsInRow);

    leftSpacing.Constant =  this.itemWidth * this.referencePoint;
    if (leftSpacing.Constant == 0)
        leftSpacing.Constant = SectionHeader.MIN_SPACING;

    base.LayoutSubviews ();
}

As you can see this is not Objective-C but you should be able to see what I'm doing. I take the width of the collection view (set on instantiation of the supplementary view) and then I calculate the width of cell which corresponds nearly to the real size of the cells. With the referencePoint I determine the position. Here you can see the setup of my constraints:

public SectionHeader (CGRect frame) : base (frame)
{
    this.width = frame.Size.Width;
    this.itemWidth  = (nfloat)Math.Round(width / numberOfItemsInRow);

    label = new UILabel (){
        BackgroundColor = UIColor.White,
        TextAlignment = UITextAlignment.Left
    };

    label.TranslatesAutoresizingMaskIntoConstraints = false;
    AddSubview (label);

    NSMutableDictionary viewsDictionary = new NSMutableDictionary();
    viewsDictionary["label"] = label;

    this.AddConstraints(NSLayoutConstraint.FromVisualFormat("V:|[label]|",(NSLayoutFormatOptions)0,null,viewsDictionary));

    leftSpacing = NSLayoutConstraint.Create(label, NSLayoutAttribute.Left, NSLayoutRelation.Equal, this, NSLayoutAttribute.Left, 1, SectionHeader.MIN_SPACING);
    leftSpacing.Priority = 250;
    this.AddConstraint(leftSpacing);
    this.AddConstraint(NSLayoutConstraint.Create(label, NSLayoutAttribute.Right, NSLayoutRelation.Equal, this, NSLayoutAttribute.Right, 1, 0));
}

In this code the calculation for the distance from the left is nearly the same, except that frame has the height of the set headerReferenceSize. The width should be OK for the initialisation. I'm using the full height for the label, I set up the leading space and I pin it to the right.

Upvotes: 4

Views: 4017

Answers (1)

Catalina T.
Catalina T.

Reputation: 3494

You need to call layoutIfNeeded on the main view in the view controller so that the constraints are updated. Something like this should work:

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    // Update the constraints here for the new orientation - toOrientation
    // ...

    [UIView animateWithDuration:duration animations: ^{
        [self.view layoutIfNeeded];
    }];
}

This will animate the layout changes on orientation change.

You could add a breakpoint after the layoutIfNeeded call, to see that the frames are what you would expect.

If your constraints are not in the view controller, but in a UIView subclass, you can do the following:

- (void)awakeFromNib {
    [super awakeFromNib];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateIntrinsicConstraints) name:UIDeviceOrientationDidChangeNotification object:nil];
}

- (void)updateIntrinsicConstraints {
    // Update your constraints here
    [self.constraint setConstant:newValue];

    // You might need to call [self layoutIfNeeded] here, if you don't call it in another place
}
- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

Let me know if this worked for you!


Update

You should not change the frame of the SectionHeader manually. You should do this by implementing the protocol method:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section;

You could call layoutIfNeeded in the init method of the SectionHeader after you add the constraints to the label, add a breakpoint after to see if the frame is correct.

Let me know if it works out for you!

Upvotes: 2

Related Questions