I’ve arrived at a solution that appears to work, although there is still an 
element I do not understand. First I corrected some calculation errors, so that 
the view sizes/centres/margins are now always calculated correctly, but that’s 
not the point. The issue was that after zooming the container view, when the 
updateConstraints method was called, the container view’s bounds did not yet 
reflect its new size. I don’t understand why, since the view had already been 
scaled (through [self scaleUnitSquareToSize:newSize]), and the new frame size 
set immediately after. After that I was a calling [self 
setNeedsUpdateConstraints:YES]. I had expected the container view’s bounds to 
reflect the new size in the methods that are subsequently called to update the 
constraints. They did not. After a bit of experimentation I replaced the call 
to setNeedsUpdateConstraints: with the following recipe which does produce the 
expected results:

            [self invalidateIntrinsicContentSize];
            [self layoutSubtreeIfNeeded];
            [self invalidateIntrinsicContentSize];

Leaving out either call to invalidateIntrinsicContentSize, or leaving out 
layoutSubtreeIfNeeded will revert to the unexpected behaviour, similar to 
calling setNeedsUpdateConstraints: only.

This feels like a hack to me, and an indication that I’m not understanding part 
of the process. If anyone has any ideas/explanations that would be awesome. (By 
the way: The container view gets its zooming by being a subclass of Graham 
Cox’s GCZoomView. Thanks Graham. The three lines of code pasted above are lines 
I added to the zoomViewByFactor:andCentrePoint method).

Also, even with the new calls to invalidate the intrinsic content size, and to 
layout the subtree, centering the subview within its parent by simply setting 
NSLayoutRelationEqual on NSLayoutAttributeCenterY, as is done for the 
horizontal centering constraint, still doesn’t work. I still need to manually 
calculate a margin from the bottom for the vertical centering.

- António

On 04 Apr 2014, at 16:48, Antonio Nunes <devli...@sintraworks.com> wrote:

> 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/devlists%40sintraworks.com
> 
> This email sent to devli...@sintraworks.com


_______________________________________________

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