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 protected])
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 [email protected]