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 (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to