• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Autolayoout on scaled views
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Autolayoout on scaled views


  • Subject: Autolayoout on scaled views
  • From: Antonio Nunes <email@hidden>
  • Date: Fri, 04 Apr 2014 16:48:46 +0100

Hi have a view that contains another view. The containerview (parent) can be scaled. The scaling is done by settings scaleUnitSquareToSize to the appropriate value. I’m trying to keep the contained view (subview, or content view) centred in the containerview. When the scale of the containerview changes, I recalculate the size of the content view and set new constraints for it:

- (void)updateContentViewConstraints
{
    SWDocumentView *contentView = self.contentView;
    NSSize requiredDisplaySizeOfDocument = contentView.requiredDisplaySize;

    if ( self.contentViewWidthConstraint ) {
        [self removeConstraint:self.contentViewWidthConstraint];
    }
    NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:contentView
                                                                  attribute:NSLayoutAttributeWidth
                                                                  relatedBy:NSLayoutRelationEqual
                                                                     toItem:nil
                                                                  attribute:NSLayoutAttributeNotAnAttribute
                                                                 multiplier:1.0
                                                                   constant:requiredDisplaySizeOfDocument.width];
    self.contentViewWidthConstraint = constraint;

    if ( self.contentViewHeightConstraint ) {
        [self removeConstraint:self.contentViewHeightConstraint];
    }
    constraint = [NSLayoutConstraint constraintWithItem:contentView
                                              attribute:NSLayoutAttributeHeight
                                              relatedBy:NSLayoutRelationEqual
                                                 toItem:nil
                                              attribute:NSLayoutAttributeNotAnAttribute
                                             multiplier:1.0
                                               constant:requiredDisplaySizeOfDocument.height];
    self.contentViewHeightConstraint = constraint;

    [self addConstraints:@[self.contentViewWidthConstraint, self.contentViewHeightConstraint]];
}

After this, I update the contsraints that should keep the contained view centred:

- (void)updateCenteringConstraints
{
    if ( self.horizontalCenteringConstraint ) {
        [self removeConstraint:self.horizontalCenteringConstraint];
    }
    if ( self.verticalCenteringConstraint ) {
        [self removeConstraint:self.verticalCenteringConstraint];
    }

    self.horizontalCenteringConstraint = [NSLayoutConstraint constraintWithItem:self.contentView
                                                                      attribute:NSLayoutAttributeCenterX
                                                                      relatedBy:NSLayoutRelationEqual
                                                                         toItem:self
                                                                      attribute:NSLayoutAttributeCenterX
                                                                     multiplier:1/self.scale
                                                                       constant:0];
    [self addConstraint:self.horizontalCenteringConstraint];

    self.verticalCenteringConstraint = [NSLayoutConstraint constraintWithItem:self.contentView
                                                                    attribute:NSLayoutAttributeCenterY
                                                                    relatedBy:NSLayoutRelationEqual
                                                                       toItem:self
                                                                    attribute:NSLayoutAttributeCenterY
                                                                   multiplier:1/self.scale
                                                                     constant:0];
    [self addConstraint:self.verticalCenteringConstraint];
}

However, this doesn’t work. The content view is centred horizontally, but not vertically. Vertically it progressively falls of the view at the bottom when zooming out, and at the top when zooming in. I don’t understand why.

I get somewhat better results if I change the code for vertically centring to this:

- (void)updateCenteringConstraints
{
    if ( self.horizontalCenteringConstraint ) {
        [self removeConstraint:self.horizontalCenteringConstraint];
    }
    if ( self.verticalCenteringConstraint ) {
        [self removeConstraint:self.verticalCenteringConstraint];
    }

    NSSize requiredDisplaySizeOfDocument = self.requiredDisplaySizeOfDocument;

    CGFloat marginV = (NSHeight(self.bounds) - (requiredDisplaySizeOfDocument.height / self.scale)) / 2.0;
    if ( marginV < 0 ) {
        marginV = kSWViewVerticalMargin / self.scale;
    }

    NSView *contentView = self.contentView;
    NSDictionary *viewsDict = NSDictionaryOfVariableBindings(contentView);

    self.horizontalCenteringConstraint = [NSLayoutConstraint constraintWithItem:self.contentView
                                                                      attribute:NSLayoutAttributeCenterX
                                                                      relatedBy:NSLayoutRelationEqual
                                                                         toItem:self
                                                                      attribute:NSLayoutAttributeCenterX
                                                                     multiplier:1/self.scale
                                                                       constant:0];
    [self addConstraint:self.horizontalCenteringConstraint];

    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[contentView]-(margin)-|"
                                                                   options:0L
                                                                   metrics:@{ @"margin" : @(marginV) }
                                                                     views:viewsDict];
    self.verticalCenteringConstraint = constraints[0];
    [self addConstraint:self.verticalCenteringConstraint];
}

Now the content view is almost centred vertically but not quite, immediately after scaling the parent view. If I subsequently change the window size, causing another layout pass, then the view is finally correctly centred vertically. Changing the window size after scaling when the former updateCenteringConstraints method (the one that uses attribute:NSLayoutAttributeCenterY to center the view vertically), does not change/correct its position.

I would have thought that asking for the content view to be centred on the containing view would be enough, and would only have to be done when originally setting up the views. When that did not work, I ensured that the centring constraints are updated each time the scale (or window size) changes, and adjust the multiplier to the scale of the parent view. This keeps the content view centred horizontally, but not vertically. Finally, I adjusted the vertical centring by manually calculating and setting the bottom margin for the content view relative to the parent view. This produces correct results, but only on the second pass after the scaling.

So, why does centring work horizontally, but not vertically? Why does the manual way of centering vertically only work on the second pass after scaling the view?

My update constraints method look like this:

- (void)updateConstraints
{
    [super updateConstraints];
    [self updateConstraintsInSuperView];
    [self updateSizeConstraints];
    [self updateContentViewConstraints];
    [self updateCenteringConstraints];
}

updateConstraintsInSuperView updates the placement of the container view in its parent view, which is an NSScrollview’s clip view, making sure it works correctly with the scroll view mechanisms:

- (void)updateConstraintsInSuperView
{
    if ( self.constraintsInSuperView ) {
        [self.superview removeConstraints:self.constraintsInSuperView];
    }
    NSView *centeringView = self;
    NSDictionary *viewsDict = NSDictionaryOfVariableBindings(centeringView);
    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[centeringView]-(<=0)-|"
                                                                   options:0L
                                                                   metrics:nil
                                                                     views:viewsDict];
    constraints = [constraints arrayByAddingObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[centeringView]-(<=0)-|"
                                                                                                     options:0L
                                                                                                     metrics:nil
                                                                                                       views:viewsDict]];
    self.constraintsInSuperView = constraints;
    [self.superview addConstraints:constraints];
}

updateSizeConstraints sizes the container view to account for its size after scaling, in such a way that it plays nicely with the scroll view, as the window is resized:

- (void)updateSizeConstraints
{
    NSView *centeringView = self;
    NSSize requiredDisplaySizeOfDocument = self.requiredDisplaySizeOfDocument;

    if ( self.widthConstraint ) {
        [self removeConstraint:self.widthConstraint];
    }
    if ( self.heightConstraint ) {
        [self removeConstraint:self.heightConstraint];
    }

    NSDictionary *viewsDict = NSDictionaryOfVariableBindings(centeringView);
    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[centeringView(>=height)]"
                                                                   options:0L
                                                                   metrics:@{ @"height" : @(requiredDisplaySizeOfDocument.height) }
                                                                     views:viewsDict];
    self.heightConstraint = constraints[0];
    [self addConstraint:self.heightConstraint];

    constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:[centeringView(>=width)]"
                                                          options:0L
                                                          metrics:@{ @"width" : @(requiredDisplaySizeOfDocument.width) }
                                                            views:viewsDict];
    self.widthConstraint = constraints[0];
    [self addConstraint:self.widthConstraint];
}

What am I overlooking?

- António


_______________________________________________

Cocoa-dev mailing list (email@hidden)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:

This email sent to email@hidden


  • Follow-Ups:
    • Re: Autolayoout on scaled views
      • From: Antonio Nunes <email@hidden>
  • Prev by Date: NSSet + NSUndoManager / Core Data = ?
  • Next by Date: Re: NSSet + NSUndoManager / Core Data = ?
  • Previous by thread: Re: NSSet + NSUndoManager / Core Data = ?
  • Next by thread: Re: Autolayoout on scaled views
  • Index(es):
    • Date
    • Thread