Reputation: 1041
I am kind of trying to get into the depths of auto layout.I have a decent knowledge of working in auto layout from storyboard.Also,I know how to use the NSLayoutConstraint class as well. Here is the problem:I have 2 views(redView and yellowView).From storyboard,I have already set the constraints of both the views.Now in my code,suppose I want to change the width of redView w.r.t the width of yellowView.So I have used the following code for that:
NSLayoutConstraint *layouts1 = [NSLayoutConstraint
constraintWithItem:_redView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:_yellowView
attribute:NSLayoutAttributeWidth
multiplier:3.0f
constant:0];
[self.view addConstraint:layouts1];
Now I run the code and though I get the expected output,it shows 'Unsatisfiable constraints' message at the console (which it should because multiple widths are being set).Now the question is :how could I get rid of that message? I have tried a couple of things but they are not working.Here is what I have tried:
Well I can write the whole auto layout code programmatically but since we have the privilege to set autolayout through storyboards,it is completely unnecessary.There must be some way just to update the constraints(set in storyboards) programmatically without getting any conflicts.
Upvotes: 0
Views: 579
Reputation: 2678
MARK: - Find Constraints
- (NSLayoutConstraint *)myConstraintWithAttribute:(NSLayoutAttribute)attribute
{
/* Find constraint with attribute in my constraints */
__block NSLayoutConstraint *resultConstraint;
[self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop)
{
// DebugLog(@"constraint %@", constraint);
if ([NSStringFromClass([NSLayoutConstraint class]) isEqualToString:NSStringFromClass([constraint class])])
{
if (constraint.firstAttribute == attribute || constraint.secondAttribute == attribute)
{
resultConstraint = constraint;
*stop = YES;
}
}
}];
return resultConstraint;
}
- (NSLayoutConstraint *)superviewConstraintWithAttribute:(NSLayoutAttribute)attribute
{
/* Find constraint with attribute in my superview's constraints */
__block NSLayoutConstraint *resultConstraint;
[self.superview.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop)
{
if (constraint.firstItem == self && constraint.firstAttribute == attribute)
//|| (constraint.secondItem == self && constraint.secondAttribute == attribute))
{
resultConstraint = constraint;
*stop = YES;
}
}];
return resultConstraint;
}
- (NSLayoutConstraint *)constraintWithAttribute:(NSLayoutAttribute)attribute
{
/* Find constraint with attribute in my constraints */
NSLayoutConstraint *resultConstraint = [self myConstraintWithAttribute:attribute];
/* Find constraint with attribute in my superview's constraints */
if (!resultConstraint)
{
resultConstraint = [self superviewConstraintWithAttribute:attribute];
}
return resultConstraint;
}
- (BOOL)removeConstraintWithAttribute:(NSLayoutAttribute)attribute
{
NSLayoutConstraint *constraint = [self superviewConstraintWithAttribute:attribute];
if (constraint)
{
[self.superview removeConstraint:constraint];
return YES;
}
constraint = [self myConstraintWithAttribute:attribute];
if (constraint)
{
[self removeConstraint:constraint];
return YES;
}
return NO;
}
MARK: - Remove Constraints
- (void)removeMyConstraints
{
/* Remove all my constraitns from superview */
[self.superview removeConstraints:[self mySuperviewConstraints]];
/* Remove my constraitns */
[self removeConstraints:self.constraints];
}
- (NSArray *)mySuperviewConstraints
{
NSMutableArray *mySuperviewConstraints = [NSMutableArray array];
[self.superview.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop)
{
if (constraint.firstItem == self || constraint.secondItem == self)
{
[mySuperviewConstraints addObject:constraint];
}
}];
return mySuperviewConstraints;
}
- (void)removeMyConstraintsButKeepMySubviewConstraints
{
/* Remove all my constraitns from superview */
[self.superview removeConstraints:[self mySuperviewConstraints]];
/* Remove my constraitns */
[self removeConstraints:[self myConstraints]];
}
- (NSArray *)myConstraints
{
NSMutableArray *myConstraints = [NSMutableArray array];
[self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop)
{
if (constraint.firstItem == self && constraint.secondItem == nil)
{
[myConstraints addObject:constraint];
}
}];
return myConstraints;
}
MARK: - Size Constraints
- (void)addWidthConstraint:(CGFloat)width
{
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:0
multiplier:1
constant:width];
[self addConstraint:constraint];
}
- (void)addWidthConstraintFromLabel:(UILabel *)label
withOffset:(CGFloat)offset
{
NSDictionary *attributes = @{NSFontAttributeName : label.font};
return [self addWidthConstraint:[label.text sizeWithAttributes:attributes].width + offset];
}
- (void)addHeightConstraint:(CGFloat)height
{
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:0
multiplier:1
constant:height];
[self addConstraint:constraint];
}
- (void)addMaximumHeightConstraint:(CGFloat)maxHeight
{
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationLessThanOrEqual
toItem:nil
attribute:0
multiplier:1
constant:maxHeight];
[self addConstraint:constraint];
}
- (void)addWidthConstraintFromImage:(UIImage *)image
{
[self addWidthConstraint:image.size.width];
}
- (void)addHeightConstraintFromImage:(UIImage *)image
{
[self addHeightConstraint:image.size.height];
}
MARK: - Center Contraints
- (void)addCenterConstraint:(UIView *)view
centerDirection:(NSLayoutAttribute)centerDirection
offset:(CGFloat)offset
{
UIView *viewItem = (view) ? view : self.superview;
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self
attribute:centerDirection
relatedBy:NSLayoutRelationEqual
toItem:viewItem
attribute:centerDirection
multiplier:1
constant:offset];
[self.superview addConstraint:constraint];
}
- (void)addCenterXConstraint:(UIView *)view
{
[self addCenterConstraint:view
centerDirection:NSLayoutAttributeCenterX
offset:0];
}
- (void)addCenterYConstraint:(UIView *)view
{
[self addCenterConstraint:view
centerDirection:NSLayoutAttributeCenterY
offset:0];
}
- (void)addCenterXConstraint:(UIView *)view
offset:(CGFloat)offset
{
[self addCenterConstraint:view
centerDirection:NSLayoutAttributeCenterX
offset:offset];
}
- (void)addCenterYConstraint:(UIView *)view
offset:(CGFloat)offset
{
[self addCenterConstraint:view
centerDirection:NSLayoutAttributeCenterY
offset:offset];
}
MARK: - Edge Attach Constraints
- (void)addEdgeAttachConstraint:(UIView *)view
viewEdge:(NSLayoutAttribute)viewLayoutAttribute
offset:(CGFloat)offset
edge:(NSLayoutAttribute)layoutAttribute
{
UIView *viewItem = (view) ? view : self.superview;
/* Reverse offset for right side and bottom */
CGFloat fixedOffset = offset;
if (layoutAttribute == NSLayoutAttributeRight
|| layoutAttribute == NSLayoutAttributeBottom
|| layoutAttribute == NSLayoutAttributeTrailing)
{
fixedOffset = -offset;
}
/* Add contraint */
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self
attribute:layoutAttribute
relatedBy:NSLayoutRelationEqual
toItem:viewItem
attribute:viewLayoutAttribute
multiplier:1
constant:fixedOffset];
[self.superview addConstraint:constraint];
}
- (void)addLeftEdgeAttachConstraint:(UIView *)view
offset:(CGFloat)offset
{
[self addEdgeAttachConstraint:view
viewEdge:NSLayoutAttributeLeft
offset:offset
edge:NSLayoutAttributeLeft];
}
- (void)addRightEdgeAttachConstraint:(UIView *)view
offset:(CGFloat)offset
{
[self addEdgeAttachConstraint:view
viewEdge:NSLayoutAttributeRight
offset:offset
edge:NSLayoutAttributeRight];
}
- (void)addTopEdgeAttachConstraint:(UIView *)view
offset:(CGFloat)offset
{
[self addEdgeAttachConstraint:view
viewEdge:NSLayoutAttributeTop
offset:offset
edge:NSLayoutAttributeTop];
}
- (void)addBottomEdgeAttachConstraint:(UIView *)view
offset:(CGFloat)offset
{
[self addEdgeAttachConstraint:view
viewEdge:NSLayoutAttributeBottom
offset:offset
edge:NSLayoutAttributeBottom];
}
- (void)addLeftEdgeAttachConstraint:(UIView *)view
{
[self addLeftEdgeAttachConstraint:view
offset:0];
}
- (void)addRightEdgeAttachConstraint:(UIView *)view
{
[self addRightEdgeAttachConstraint:view
offset:0];
}
- (void)addTopEdgeAttachConstraint:(UIView *)view
{
[self addTopEdgeAttachConstraint:view
offset:0];
}
- (void)addBottomEdgeAttachConstraint:(UIView *)view
{
[self addBottomEdgeAttachConstraint:view
offset:0];
}
- (void)addEdgeAttachConstraints:(UIView *)view
leftOffset:(CGFloat)leftOffset
rightOffset:(CGFloat)rightOffset
topOffset:(CGFloat)topOffset
bottomOffset:(CGFloat)bottomOffset
{
[self addLeftEdgeAttachConstraint:view
offset:leftOffset];
[self addRightEdgeAttachConstraint:view
offset:rightOffset];
[self addTopEdgeAttachConstraint:view
offset:topOffset];
[self addBottomEdgeAttachConstraint:view
offset:bottomOffset];
}
- (void)addEdgeAttachConstraints:(UIView *)view
{
[self addLeftEdgeAttachConstraint:view];
[self addRightEdgeAttachConstraint:view];
[self addTopEdgeAttachConstraint:view];
[self addBottomEdgeAttachConstraint:view];
}
MARK: - Edge Constraint To Different Edge
- (void)addLeftEdgeAttachConstraint:(UIView *)view
viewEdge:(NSLayoutAttribute)viewLayoutAttribute
offset:(CGFloat)offset
{
[self addEdgeAttachConstraint:view
viewEdge:viewLayoutAttribute
offset:offset
edge:NSLayoutAttributeLeft];
}
- (void)addRightEdgeAttachConstraint:(UIView *)view
viewEdge:(NSLayoutAttribute)viewLayoutAttribute
offset:(CGFloat)offset
{
[self addEdgeAttachConstraint:view
viewEdge:viewLayoutAttribute
offset:offset
edge:NSLayoutAttributeRight];
}
- (void)addTopEdgeAttachConstraint:(UIView *)view
viewEdge:(NSLayoutAttribute)viewLayoutAttribute
offset:(CGFloat)offset
{
[self addEdgeAttachConstraint:view
viewEdge:viewLayoutAttribute
offset:offset
edge:NSLayoutAttributeTop];
}
- (void)addBottomEdgeAttachConstraint:(UIView *)view
viewEdge:(NSLayoutAttribute)viewLayoutAttribute
offset:(CGFloat)offset
{
[self addEdgeAttachConstraint:view
viewEdge:viewLayoutAttribute
offset:offset
edge:NSLayoutAttributeBottom];
}
MARK: - Size Attach Constaints
- (void)addSizeAndSuperviewAttachConstraints:(NSString *)sizeConstraint
firstOffset:(CGFloat)firstOffset
secondOffset:(CGFloat)secondOffset
direction:(NSString *)direction
{
NSDictionary *viewDict = NSDictionaryOfVariableBindings(self);
NSString *visualFormatString;
if (sizeConstraint)
{
visualFormatString = [NSString stringWithFormat:@"%@:|-%f-[self(%@)]-%f-|", direction, firstOffset, sizeConstraint, secondOffset];
}
else
{
visualFormatString = [NSString stringWithFormat:@"%@:|-%f-[self]-%f-|", direction, firstOffset, secondOffset];
}
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:visualFormatString
options:0
metrics:0
views:viewDict];
[self.superview addConstraints:constraints];
}
- (void)addWidthAndSuperviewAttachConstraints:(NSString *)widthConstraint
leftOffset:(CGFloat)leftOffset
rightOffset:(CGFloat)rightOffset
{
[self addSizeAndSuperviewAttachConstraints:widthConstraint
firstOffset:leftOffset
secondOffset:rightOffset
direction:@"H"];
}
- (void)addHeightAndSuperviewAttachConstraints:(NSString *)heightConstraint
topOffset:(CGFloat)topOffset
bottomOffset:(CGFloat)bottomOffset
{
[self addSizeAndSuperviewAttachConstraints:heightConstraint
firstOffset:topOffset
secondOffset:bottomOffset
direction:@"V"];
}
MARK: - Row & Column Layout Constraints
- (void)addLayoutConstraintsForMySubviews:(NSArray *)views
firstOffset:(CGFloat)firstOffset
secondOffset:(CGFloat)secondOffset
betweenOffset:(NSString *)betweenOffset
direction:(NSString *)direction
equalSize:(BOOL)equalSize
{
/* Create viewDict and visualFormatString */
NSMutableString *visualFormatString = [[NSMutableString alloc] initWithFormat:@"%@:|-%.0f-", direction, firstOffset];
NSMutableDictionary *viewDict = [[NSMutableDictionary alloc] init];
[views enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop)
{
NSString *viewName = [NSString stringWithFormat:@"view%i", idx];
[viewDict setObject:view
forKey:viewName];
if (idx < [views count] - 1)
{
/* Add each view */
if (betweenOffset) /* Add offset between view */
{
/* Add equal size to prev view for all but index 0 */
if (equalSize && idx > 0)
{
NSString *prevViewName = [NSString stringWithFormat:@"view%i", idx - 1];
[visualFormatString appendFormat:@"[%@(==%@)]-%@-", viewName, prevViewName, betweenOffset];
}
else
{
[visualFormatString appendFormat:@"[%@]-%@-", viewName, betweenOffset];
}
}
else /* No offset between views */
{
/* Add equal size to prev view for all but index 0 */
if (equalSize && idx > 0)
{
NSString *prevViewName = [NSString stringWithFormat:@"view%i", idx - 1];
[visualFormatString appendFormat:@"[%@(==%@)]", viewName, prevViewName];
}
else
{
[visualFormatString appendFormat:@"[%@]", viewName];
}
}
}
else
{
/* Add equal size to prev view for all but index 0 */
if (equalSize && idx > 0)
{
NSString *prevViewName = [NSString stringWithFormat:@"view%i", idx - 1];
[visualFormatString appendFormat:@"[%@(==%@)]-%.0f-|", viewName, prevViewName, secondOffset];
}
else
{
[visualFormatString appendFormat:@"[%@]-%.0f-|", viewName, secondOffset];
}
}
}];
// DebugLog(@"viewDict %@", viewDict);
// DebugLog(@"visualFormatString %@", visualFormatString);
/* Add constraints */
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:visualFormatString
options:0
metrics:0
views:viewDict];
[self addConstraints:constraints];
}
- (void)addRowLayoutConstraintsForMySubviews:(NSArray *)subviews
leftOffset:(CGFloat)leftOffset
rightOffset:(CGFloat)rightOffset
betweenOffset:(NSString *)betweenOffset
equalWidth:(BOOL)equalWidth
{
[self addLayoutConstraintsForMySubviews:subviews
firstOffset:leftOffset
secondOffset:rightOffset
betweenOffset:betweenOffset
direction:@"H"
equalSize:equalWidth];
}
- (void)addColumnLayoutConstraintsForMySubviews:(NSArray *)subviews
topOffset:(CGFloat)topOffset
bottomOffset:(CGFloat)bottomOffset
betweenOffset:(NSString *)betweenOffset
equalHeight:(BOOL)equalHeight
{
[self addLayoutConstraintsForMySubviews:subviews
firstOffset:topOffset
secondOffset:bottomOffset
betweenOffset:betweenOffset
direction:@"V"
equalSize:equalHeight];
}
MARK: - Row & Column Equal Size Layout Constraints
- (void)addEqualSizeLayoutConstraintsForMySubviews:(NSArray *)views
firstOffset:(CGFloat)firstOffset
secondOffset:(CGFloat)secondOffset
betweenOffset:(NSString *)betweenOffset
direction:(NSString *)direction
{
/* Create viewDict and visualFormatString */
NSMutableString *visualFormatString = [[NSMutableString alloc] initWithFormat:@"%@:|-%.0f-", direction, firstOffset];
NSMutableDictionary *viewDict = [[NSMutableDictionary alloc] init];
[views enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop)
{
NSString *viewName = [NSString stringWithFormat:@"view%i", idx];
[viewDict setObject:view
forKey:viewName];
if (idx < [views count] - 1)
{
if (betweenOffset)
{
[visualFormatString appendFormat:@"[%@]-%@-", viewName, betweenOffset];
}
else
{
[visualFormatString appendFormat:@"[%@(>=40)]", viewName];
}
}
else
{
[visualFormatString appendFormat:@"[%@(>=40)]-%.0f-|", viewName, secondOffset];
}
}];
// DebugLog(@"viewDict %@", viewDict);
/* Add constraints */
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[view0]-2-[view1(==view0)]-2-[view2(==view1)]-2-[view3(==view2)]-2-[view4(==view3)]-2-[view5(==view4)]-0-|"
options:0
metrics:0
views:viewDict];
[self addConstraints:constraints];
}
- (void)addRowLayoutEqualWidthConstraintsForMySubviews:(NSArray *)subviews
leftOffset:(CGFloat)leftOffset
rightOffset:(CGFloat)rightOffset
betweenOffset:(NSString *)betweenOffset
{
[self addEqualSizeLayoutConstraintsForMySubviews:subviews
firstOffset:leftOffset
secondOffset:rightOffset
betweenOffset:betweenOffset
direction:@"H"];
}
Upvotes: 0
Reputation: 10160
Am I understanding you correctly when you want to edit an existing constraint programmatically? I.e. a constraint set from storyboard?
You can make a IBOutlet
from the constraint, and set its constant
-property programmatically. Then call layotIfNeeded
.
Upvotes: 1
Reputation: 1041
Well I understand what I was trying to do.As I had already set the constraints in the storyboard,I need to remove that width constraint(in storyboard) to update it with a new constraint.SO I just created an IBOutlet of the width of my yellowView and then this code did the trick for me:
[_yellowView removeConstraint:_myConstraints];//_myConstraints is the outlet
Now I'm not facing any problem.Thanks a lot guys :-)
Upvotes: 0