Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use NSNumber or CGFloat constant in Autolayout visual format language?

I'm trying to set top and bottom paddings to a view in Autolayout using the visual format language. The code compiles and works if I write the paddings as integers in visual format string, but it fails when I try to replace it with a constant. Here's the error I'm getting:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse constraint format: It's not possible to set a space equal to the width or height of a view. Perhaps you want to use a view as a spacer? [view1][spacer(==view1)][view2] V:|-kTopAndBottomPadding-[messageTextView]-kTopAndBottomPadding-|

And this is my code.

CGFloat const SPMTVC_kTopAndBottomPadding = 5.0;
// ...

// Create my own NSDictionary of variable bindings.
NSDictionary *variableBindings = @{@"messageTextView" : _messageTextView,
    @"contentView" : self.contentView,
    @"kTopAndBottomPadding" : [NSNumber numberWithFloat:SPMTVC_kTopAndBottomPadding]};

// Constraints in the horizontal axis.
// Basically just pins the view to the left and right of superview.
NSMutableArray *constraints = [[NSLayoutConstraint
    constraintsWithVisualFormat:@"H:|-0-[messageTextView(==contentView)]-0-|"
    options:NSLayoutFormatAlignAllLeading
    metrics:nil
    views:variableBindings] mutableCopy];

// Constraints in vertical axis, give 5-point padding from superview's top & bottom.
[constraints addObjectsFromArray:[NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-kTopAndBottomPadding-[messageTextView]-kTopAndBottomPadding-|"
    options:NSLayoutFormatAlignAllTop
    metrics:nil
    views:variableBindings]];

for (NSLayoutConstraint *constraint in constraints) {
    [self.contentView addConstraint:constraint];
}

I think the error message means that the compiler thinks kTopAndBottomPadding is a UIView, when it's an NSNumber as defined in the dictionary. Is there a way to do this right?

like image 532
MLQ Avatar asked Aug 12 '13 05:08

MLQ


2 Answers

This is what the metrics dictionary is for. So for example, if you want to use "padding" as a constant in your VFL string, do something like this:

[constraints addObjectsFromArray:[NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-padding-[messageTextView]-kTopAndBottomPadding-|"
    options:NSLayoutFormatAlignAllTop
    metrics:@{@"padding":@5}
    views:variableBindings]];
like image 131
rdelmar Avatar answered Nov 20 '22 19:11

rdelmar


You need to pass kTopAndBottomPadding in the metrics dictionary, not the views dictionary.

Also, you might want to just take advantage of the addConstraints: method instead of messing about with mutable arrays and for/in loops.

CGFloat const SPMTVC_kTopAndBottomPadding = 5.0;
// ...

NSDictionary *metrics = @{
    @"kTopAndBottomPadding" : @(SPMTVC_kTopAndBottomPadding)
};
NSDictionary *views = @{
    @"messageTextView" : _messageTextView,
    @"contentView" : self.contentView
};

[self.contentView addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"H:|-0-[messageTextView(==contentView)]-0-|"
    options:NSLayoutFormatAlignAllLeading
    metrics:metrics views:views]];

[self.contentView addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-kTopAndBottomPadding-[messageTextView]-kTopAndBottomPadding-|"
    options:NSLayoutFormatAlignAllTop
    metrics:metrics views:views]];
like image 31
rob mayoff Avatar answered Nov 20 '22 20:11

rob mayoff