Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deal with NSLayoutConstraint verbosity in code?

Tags:

ios

autolayout

I have come to appreciate the work that Auto Layout does for me, but I'm with Brent Simmons on the topic of not using Interface Builder to set up the constraints. The interface as provided by Apple is flexible but extremely verbose — clearly designed for code generator instead of human use. For me it exemplifies the worst of Objective-C with repeating overlong identical prefixes and rarely used parameters obfuscating all meaning instead of providing clarity to the code. I've seen Florian Kugler's FLKAutoLayout that hides the constraint creation in category on UIView.

Are there other ways to make the layout constraints in code cleaner and easier to understand?

like image 361
Palimondo Avatar asked Dec 27 '22 00:12

Palimondo


1 Answers

I tried to stay as close as possible to the Apple's implementation, acknowledging the existence of NSLayoutConstraint, so I have simply defined a set of Concise Auto Layout macros that remove the prefixes from attributes and relations and wrap the constraint creation in a constructor function-like macros (also omitting the multiplier parameter that I never use):

  • Constraint
  • ConstantConstraint
  • VisualConstraints
  • VisualConstraintWithMetrics

To attach multiple simple constraints to view, I wrap them in array literal. I always prefix non-zero constants with a sign, to emphasize the displacement. In practice it looks like this (instance variables reffer to views):

[self.view addConstraints:
 @[Constraint(_verticalSeparator, CenterX, Equal, _top, CenterX, 0),
 Constraint(_verticalSeparator, CenterY, Equal, _top, CenterY, +22),
 Constraint(_verticalSeparator, Height, Equal, _localWeather, Height, 0),
 Constraint(_localWeather, CenterY, Equal, _verticalSeparator, CenterY, 0),
 Constraint(_addLocation, CenterY, Equal, _verticalSeparator, CenterY, 0),
 Constraint(_touchDown, Trailing, Equal, _verticalSeparator, Trailing, -1),
 Constraint(_touchDown, CenterY, Equal, _localWeather, CenterY, 0),
 Constraint(_touchDown, Width, Equal, _localWeather, Width, +26),
 Constraint(_touchDown, Height, Equal, _localWeather, Height, 0)
]];

id f = @"[_localWeather]-space-[_verticalSeparator]-space-[_addLocation]";
[self.view addConstraints:
 VisualConstraintWithMetrics(f, @{@"space": @11},
                             _localWeather, _verticalSeparator, _addLocation)];


[_tableCell addConstraint:ConstantConstraint(_tableCell, Height, Equal, 44)];

When dealing with group of views that all share the same setup, I enumerate over array literal like this:

[@[_top, _middle, _bottom, _touchDown, _verticalSeparator]
 enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
    view.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:view];
}];

Filling the superview horizontally happened often enough, that its lifted into its own macro:

[@[_top, _middle, _bottom] enumerateObjectsUsingBlock:horizontallyFillSuperview];

I'll update this answer as my style evolves…

like image 51
Palimondo Avatar answered Jan 08 '23 23:01

Palimondo