Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is NSLayoutAttributeCenterX an 'Invalid pairing' with NSLayoutAttributeWidth

I am trying to have the auto layout manager adjust the center point of a view, based on the width of the super view. I don't understand why that is an 'Invalid Pairing' of the attributes (as told by the crash and NSInvalidArgumentException)

UIView *ac;
NSLayoutConstraint *cXloc = [NSLayoutConstraint constraintWithItem:ac 
                                                         attribute:NSLayoutAttributeCenterX 
                                                         relatedBy:NSLayoutRelationEqual 
                                                            toItem:ac.superview 
                                                         attribute:NSLayoutAttributeWidth 
                                                        multiplier:.1 
                                                          constant:x*ac.superview.frame.size.width*.2];
[ac.superview addConstraint:cXloc];

Could someone explain why this is an 'Invalid paring' and how I should approach this? Thanks

like image 765
MobileVet Avatar asked Dec 13 '12 20:12

MobileVet


3 Answers

It is the limitation of the current implementation of Auto Layout. However, you can easily work around it since all the constrains are linear and NSLayoutAttributes are correlated. For example, say, the constraint you want is:

subview.centerX = m * superview.width + c;

You can express it as a relationship between tow centerXs:

// Since width == 2 * centerX
subview.centerX = m * 2 * superview.centerX + c;
like image 132
an0 Avatar answered Nov 04 '22 03:11

an0


If you relate ac's AttributeCenterX to its superview's AttributeCenterX, AttributeLeading, or AttributeTrailing, you should be able to express your desired constraint using the multiplier and constraint. Keep in mind that the constant is evaluated only when the constraint is created, and your example's constant wouldn't update as ac.superview's width changes.

If you can express in words how you'd like ac positioned relative to its superview, we can suggest a constraint.

Edit

Here's an example with 5 NSButtons. They themselves and the space between them expand so that the spaces are 30% as wide as the buttons, all the buttons have the same width, and all the spaces have the same width. Creating 4 invisible NSViews just for spacing is pretty cumbersome, especially considering you've got it working outside of autolayout. But in case you're curious:

// Assuming these NSViews and NSButtons exist,
//NSView* superview ;
//NSButton *buttonOne, *buttonTwo, *buttonThree, *buttonFour, *buttonFive ;


[superView removeConstraints:superView.constraints] ;

// Create empty NSViews to fill the space between the 5 buttons.
NSView* spaceOne = [NSView new] ;
NSView* spaceTwo = [NSView new] ;
NSView* spaceThree = [NSView new] ;
NSView* spaceFour = [NSView new] ;
spaceOne.translatesAutoresizingMaskIntoConstraints = NO ;
spaceTwo.translatesAutoresizingMaskIntoConstraints = NO ;
spaceThree.translatesAutoresizingMaskIntoConstraints = NO ;
spaceFour.translatesAutoresizingMaskIntoConstraints = NO ;
[superView addSubview:spaceOne] ;
[superView addSubview:spaceTwo] ;
[superView addSubview:spaceThree] ;
[superView addSubview:spaceFour] ;

NSDictionary* views = NSDictionaryOfVariableBindings(superView,buttonOne,buttonTwo,buttonThree,buttonFour,buttonFive,spaceOne,spaceTwo,spaceThree,spaceFour) ;

// Vertically align buttonOne to its superview however you like.
[superView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[buttonOne]"  options:0  metrics:nil  views:views ] ] ;

// Make the "space" NSViews' widths equal and >= 10. Make the buttons' widths equal.
[superView addConstraints: [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[buttonOne][spaceOne(>=10)][buttonTwo(==buttonOne)][spaceTwo(==spaceOne)][buttonThree(==buttonOne)][spaceThree(==spaceOne)][buttonFour(==buttonOne)][spaceFour(==spaceOne)][buttonFive(==buttonOne)]|"  options: NSLayoutFormatAlignAllCenterY  metrics:nil  views:views ] ] ;
// Make the "space" NSViews' widths 30% of the NSButtons' widths.
[superView addConstraint: [NSLayoutConstraint constraintWithItem: spaceOne
                                                       attribute: NSLayoutAttributeWidth
                                                       relatedBy: NSLayoutRelationEqual
                                                          toItem: buttonOne
                                                       attribute: NSLayoutAttributeWidth
                                                      multiplier: 0.3
                                                        constant: 0 ] ] ;
like image 3
John Sauer Avatar answered Nov 04 '22 05:11

John Sauer


Based on an0's answer, and assuming you have an NSArray containing your buttons, the following should space the buttons equally within the superview:

  NSUInteger currentButton = 1;
  for (UIButton *button in self.buttons)
  {
    [self.view addConstraint:[NSLayoutConstraint constraintWithItem:button attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:2.0 * (CGFloat) currentButton / (CGFloat) (self.buttons.count + 1) constant:0.0]];
    currentButton++;
  }
like image 1
mharper Avatar answered Nov 04 '22 04:11

mharper