I am trying to center a view using the visual format language.
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_progressView(300)]-|" options:NSLayoutFormatAlignAllCenterY metrics:0 views:views]];
(views
is a NSDictionaryOfVariableBindings
containing _progressView
)
It does not center my view (width: 300) within the self.view
(width: 768). It aligns it left with an 8 pixel margin, as if I would have only written @"H:|-[_progressView(300)"
.
Is the only way to center the view using something like:?
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_progressView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual
toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
Thanks
This format string
@"H:|-[_progressView(300)]-|"
doesn't tell AutoLayout to center progressView
. Instead it says that progressView
should be 300 wide and have a system defined standard margin on either side to the superview's edges. Obviously this can't be satisfied, so Auto Layout drops some of the constraints (you probably get some logs on the console). The constraint that is dropped in your case is probably the margin on the right.
To really center a view in it's superview you have to use the verbose constraint method instead of the visual string format, as you already figured out. However, you can easily put that into a nice category like this:
@interface UIView (MyLayout)
- (void)centerHorizontallyInSuperview;
@end
@implementation UIView (MyLayout)
- (void)centerHorizontallyInSuperview {
NSLayoutConstraint *c;
c = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.superview
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0];
[self.superview addConstraint:c];
}
@end
Three approaches:
You can put your constraint adding code in a method or category as suggested by DrummerB.
You also, effective iOS 9, can use a newer, more concise syntax, using anchors and using activateConstraints
:
[NSLayoutConstraint activateConstraints:@[
[centerView.centerXAnchor constraintEqualToAnchor:view.centerXAnchor],
...
]];
Or in Swift 3:
NSLayoutConstraint.activate([
centerView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
...
])
You can use UILayoutGuide
objects:
UILayoutGuide *leftGuide = [[UILayoutGuide alloc] init];
[view addLayoutGuide:leftGuide];
UILayoutGuide *rightGuide = [[UILayoutGuide alloc] init];
[view addLayoutGuide:rightGuide];
and define them to be equal to each other with VFL like so:
H:|[leftGuide][_progressView(==300)][rightGuide(==leftGuide)]|
If you can't use layout guides (e.g. you need to support iOS versions prior to 9.0, you want to create this in IB, etc.) you can use non-visible UIView
objects (alpha of zero or background color of clear) in a very similar manner. Just add these "spacer" views to your view hierarchy, use horizontal constraints like above, but simply configure the views so you can't see them.
This technique would be a cumbersome way of solving the "how do a center a view" problem, but is extremely useful if you, for example, want to evenly space a dozen views and you can't use a UIStackView
for some reason. You can use however many UILayoutGuide
(or invisible UIView
) objects that you want in order to achieve the desired effect.
For the sake of completeness, I should point out that it's possible to achieve the effect by using options
of NSLayoutFormatAlignAllCenterX
/Y
, as outlined in https://stackoverflow.com/a/14917695/1271826, but I confess that I find it unnecessarily confusing.
Personally, I think the first approach makes the most sense for your scenario. The second approach is useful when trying to evenly space a bunch of controls, but is too clumsy for this simple scenario. The third approach is an intellectual curiosity, but I wouldn't suggest it in any real-world application.
For those who need it, @DrummerB's ObjC category as Swift extension:
extension UIView {
func centerInSuperview() {
self.centerHorizontallyInSuperview()
self.centerVerticallyInSuperview()
}
func centerHorizontallyInSuperview(){
let c: NSLayoutConstraint = NSLayoutConstraint(item: self, attribute: .CenterX, relatedBy: .Equal, toItem: self.superview, attribute: .CenterX, multiplier: 1, constant: 0)
self.superview?.addConstraint(c)
}
func centerVerticallyInSuperview(){
let c: NSLayoutConstraint = NSLayoutConstraint(item: self, attribute: .CenterY, relatedBy: .Equal, toItem: self.superview, attribute: .CenterY, multiplier: 1, constant: 0)
self.superview?.addConstraint(c)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With