I have a UIView that I want to mask with another UIView, punching a hole out of its center. Here's my viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.viewToMask];
[self.view addSubview:self.theMask];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.viewToMask attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.viewToMask attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[cyan(200)]" options:0 metrics:nil views:#{#"cyan": self.viewToMask}]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[cyan(200)]" options:0 metrics:nil views:#{#"cyan": self.viewToMask}]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.theMask attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.viewToMask attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.theMask attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.viewToMask attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[mask(100)]" options:0 metrics:nil views:#{#"mask": self.theMask}]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[mask(100)]" options:0 metrics:nil views:#{#"mask": self.theMask}]];
}
It gives me exactly what I'm looking for, minus the masking:
If I add one more line:
[self.viewToMask setMaskView:self.theMask];
both views disappear --- the small view (self.theMask) masks out the whole larger view (self.viewToMask) even though it's only half the size. Does anyone understand what's going on here? Can you not use UIView.maskView with Auto Layout?
As Zev explained, the mask view lives outside of the ordinary view hierarchy and so can't be used together with Auto Layout. I got around this by placing it manually in my view controller's viewDidLayoutSubviews:
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
CGRect viewToMaskRect = self.viewToMask.bounds;
CGRect maskRect = CGRectMake(viewToMaskRect.origin.x + 50.0, viewToMaskRect.origin.y + 50.0, 100.0, 100.0);
[self.theMask setFrame:maskRect];
[self.viewToMask setMaskView:self.theMask];
}
Based on Russ' answer I did my own research and got an alternative workaround.
The code sample below will mask the left and right side of the main view with 10pt. Important
class SomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.whiteColor()
self.setupMaskView()
}
private func setupMaskView() {
let maskView = UIView()
maskView.translatesAutoresizingMaskIntoConstraints = false
maskView.backgroundColor = UIColor(white: 0.0, alpha: 1.0)
let maskContainerView = UIView()
maskContainerView.addSubview(maskView)
self.view.addSubview(maskContainerView)
maskView.leftAnchor.constraintEqualToAnchor(self.view.leftAnchor, constant: 10).active = true
maskView.widthAnchor.constraintEqualToAnchor(self.view.widthAnchor, constant: -20).active = true
maskView.heightAnchor.constraintEqualToAnchor(self.view.heightAnchor).active = true
self.view.maskView = maskContainerView // this will not work it we place this line above the constraint code !!!
/* I have no idea why this does not work and why nesting is required, maybe some sort of bug? */
}
}
This code is using AutoLayout syntax available from iOS 9.0 and Swift 2.0. (Written in Xcode 7 beta 4)
You can make auto layout work with your maskView by making the maskView just a normal subview (Eg. in a storyboard with relevant constraints) then only setting it as maskView in viewWillLayoutSubviews. Ie:
-(void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
self.viewToBeMasked.maskView = self.maskViewWithLayoutConstraints;
}
Related
I don't know what I'm doing wrong: I'm creating a UIView that occupies all the screen (it has already constraints) and then, programmatically I'm creating an UI Image View:
_panel = [[UIImageView alloc] initWithImage:[self loadImageForKey:#"registerPanel"]];
_panel.frame = CGRectMake(0, 0, 100, 100);
_panel.exclusiveTouch = YES;
_panel.userInteractionEnabled = YES,
[self.scrollView addSubview:_panel];
And here it comes the problem: I'm adding constraints to the panel I created but it crashes (I'm doing it on the ViewWillAppear):
NSLayoutConstraint *centreHorizontallyConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
NSLayoutConstraint *centreVerticalConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
[_panel addConstraint:centreHorizontallyConstraint];
[_panel addConstraint:centreVerticalConstraint];
Error message:
When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug.
You can constrain a scrollView's subview to the scrollView's parent (self.view in this case), but that's probably not what you want.
Edit: For clarification, the reason you were getting the error was because you initialize your constraints:
toItem:self.view
and then you try to add them:
[_panel addConstraint:centreHorizontallyConstraint];
[_panel addConstraint:centreVerticalConstraint];
You want to add them to the toItem object:
[self.view addConstraint:centreHorizontallyConstraint];
[self.view addConstraint:centreVerticalConstraint];
Again, you probably don't want to center _panel in the main view, but this will compile and run:
#import "AddPanelScrollViewController.h" /// just default .h
#interface AddPanelScrollViewController ()
#property (strong, nonatomic) UIScrollView *scrollView;
#property (strong, nonatomic) UIImageView *panel;
#end
#implementation AddPanelScrollViewController
- (void)viewDidLoad {
[super viewDidLoad];
_scrollView = [UIScrollView new];
_scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_scrollView];
[_scrollView.topAnchor constraintEqualToAnchor:self.view.topAnchor constant:20.0].active = YES;
[_scrollView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor constant:-20.0].active = YES;
[_scrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20.0].active = YES;
[_scrollView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20.0].active = YES;
_scrollView.backgroundColor = [UIColor blueColor];
_panel = [UIImageView new];
// required
_panel.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:_panel];
// frame will be ignored when using auto-layout / constraints
// _panel.frame = CGRectMake(0, 0, 100, 100);
_panel.exclusiveTouch = YES;
_panel.userInteractionEnabled = YES;
_panel.backgroundColor = [UIColor redColor];
// _panel needs width and height constraints
[_panel.widthAnchor constraintEqualToConstant:100.0].active = YES;
[_panel.heightAnchor constraintEqualToConstant:100.0].active = YES;
NSLayoutConstraint *centreHorizontallyConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
NSLayoutConstraint *centreVerticalConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0];
// if constraints are releated to "self.view" that's where they need to be added
[self.view addConstraint:centreHorizontallyConstraint];
[self.view addConstraint:centreVerticalConstraint];
}
First you can't create constraints between panel & self.view because there is no common parent , instead you want to create them with the scrollview
NSLayoutConstraint *centreHorizontallyConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
NSLayoutConstraint *centreVerticalConstraint = [NSLayoutConstraint
constraintWithItem:_panel
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0];
[_scrollView addConstraint:centreHorizontallyConstraint];
[_scrollView addConstraint:centreVerticalConstraint];
Also both constraints are centerX , you need also width & height , or better top , leading , trailing and bottom to scrollView ,,, with width and height static or proportional to self.view
//
Also for any view you want to add constraints programmatically you must set
[self.scrollView setTranslatesAutoresizingMaskIntoConstraints: NO];
[self.panel setTranslatesAutoresizingMaskIntoConstraints: NO];
I have added my own custom uiview to self.view. As we can't set constraints in storyboard in this scenario, i tried to add programatically.
Followed this How to Create layout constraints programmatically.
I am using below code.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
[customView setNeedsUpdateConstraints];
}
-(void)updateViewConstraints
{
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[self.view addConstraints:#[
//view1 constraints
[NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:customView
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],
]];
}
I am calling [self updateViewConstraints] from viewDidLoad,but still i am getting half view in landscape.
Any idea on this. Thanks in Advance.
You can use Masonry for this purposes. As example:
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);}];
This will set top, right, bottom and left constraints for your view.
try in willRotateToInterfaceOrientation:duration:call setNeedsUpdateConstraints to view that needs its constraints updated.
Unless view is not notified it wont update constraints
I your constraints are set correctly, the view will resize according to them when the user changes the orientation.
If you show us the your code, it would be easier for us to help you.
Some hints:
Be sure to set the translatesAutoresizingMaskIntoConstraints property to NO
You can use the visual format to set your constraints, it is sometimes easier not to forget one : Visual Format Language
Check your logs to see if a constraint is not broken
If your constraints are set correctly, the view should be resized according to its superview frame.
UPDATE
I see two possibilities: either the constraints are not really set (because the view is not prepared for instance), either one constraint or more are broken (but you should see some logs in that case).
I give you some code that works for me (I personally use Visual Format for constraints when possible) :
customView = [UIView new];
customView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:customView];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
NSDictionary *views = #{ #"customView" : customView };
NSDictionary *metrics = #{ #"top" : #(padding.top) , #"bottom" : #(padding.bottom) , #"right" : #(padding.right) , #"left" : #(padding.left) };
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-left-[customView]-right-|" options:0 metrics:nil views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-top-[customView]-bottom-|" options:0 metrics:nil views:views]];
I don't use the method [customView setNeedsUpdateConstraints]; when a rotation occurred : the system updates the constraints of the subviews by itself.
I'm trying to understand how auto layout works programmatically. I know we can assign it in the Interface Builder but I want to learn more about how it works underneath.
If someone could show me the code to make a UIImageView pin it's top, left, right, and bottom alignments (as well as equal width and equal height) that would be great!
yourView.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *views = NSDictionaryOfVariableBindings(yourView);
[superview addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|[yourView]|"
options:(NSLayoutFormatAlignAllLeading | NSLayoutFormatAlignAllTrailing)
metrics:nil
views:views]];
[superview addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[yourView]|"
options:(NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom)
metrics:nil
views:views]];`
You can apply the constraints using native NSLayoutConstraints as follows:
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:#[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],
]];
This would be very complex for the beginners, one easy way to achieve this with few lines of code is to use autolayout wrappers like Masonry.
With Masonry, you can do the same as above :
Heres the same constraints created using MASConstraintMaker
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
Or even shorter
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
I need to locate a subview vertical align middle in a view without hardcode the top value 150 using constraint programmatically. I wish to achieve something like below:
Below is my code so far, Please advise.
import UIKit
class MainViewController: UIViewController {
var viewInner:UIView = UIView()
var viewOuter:UIView = UIView()
override func viewDidLoad() {
super.viewDidLoad()
viewInner.backgroundColor = UIColor.redColor()
viewOuter.backgroundColor = UIColor.purpleColor()
viewOuter.frame = CGRectMake(100, 100, 400, 400)
viewInner.setTranslatesAutoresizingMaskIntoConstraints(false)
let viewsDictionary = ["viewInner":viewInner]
viewOuter.addSubview(viewInner)
self.view.addSubview(viewOuter)
//Add Constraint
var constOuterH = NSLayoutConstraint.constraintsWithVisualFormat("H:|-10-[viewInner(>=200)]-10-|", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary)
var constOuterV = NSLayoutConstraint.constraintsWithVisualFormat("V:|-150-[viewInner(100)]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary)
viewOuter.addConstraints(constOuterH)
viewOuter.addConstraints(constOuterV)
}
}
You need to use the non-visual formatting way of adding constraints:
[self.view addConstraint:[NSlayoutConstraint constraintWithItem:self.viewOuter attribute:NSLayoutAttributeCenterY relateBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY .......]];
to center align things.
The above type of "relational constraints" (it's what I personally call them) is what I tend to use for specifying relations between two views e.g. center alignment, relative positioning.
Visual Formatting language on the other hand is more useful for pinning views to each other from my own observation.
Here's a demo app:
ViewController.h
#interface ViewController : UIViewController
#property (nonatomic, strong) UIView *outerView;
#property (nonatomic, strong) UIView *innerView;
#property (nonatomic, strong) NSLayoutConstraint *outerViewHeightConstraint;
#end
ViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initViews];
[self initConstraints];
// change height of outerView after 3 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self changeHeight];
});
}
-(void)changeHeight
{
self.outerViewHeightConstraint.constant = 150;
[UIView animateWithDuration:1.0 animations:^{
[self.innerView layoutIfNeeded];
[self.view layoutIfNeeded];
}];
}
-(void)initViews
{
self.outerView = [[UIView alloc] init];
self.outerView.backgroundColor = [UIColor purpleColor];
self.innerView = [[UIView alloc] init];
self.innerView.backgroundColor = [UIColor redColor];
[self.outerView addSubview:self.innerView];
[self.view addSubview:self.outerView];
}
-(void)initConstraints
{
self.outerView.translatesAutoresizingMaskIntoConstraints = NO;
self.innerView.translatesAutoresizingMaskIntoConstraints = NO;
id views = #{
#"outerView": self.outerView,
#"innerView": self.innerView
};
// outer view constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[outerView(300)]" options:0 metrics:nil views:views]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.outerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.outerView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
// give outerView a default height e.g. 300
// note we can animate the height of outerview later using this var
self.outerViewHeightConstraint = [NSLayoutConstraint constraintWithItem:self.outerView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeHeight multiplier:1.0 constant:300.0];
[self.view addConstraint:self.outerViewHeightConstraint];
// inner view constraints
[self.outerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-10-[innerView]-10-|" options:0 metrics:nil views:views]];
[self.outerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[innerView(50)]" options:0 metrics:nil views:views]];
[self.outerView addConstraint:[NSLayoutConstraint constraintWithItem:self.innerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.outerView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.outerView addConstraint:[NSLayoutConstraint constraintWithItem:self.innerView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.outerView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
}
After running the app for 3 seconds, you'll see the outer view (purple view) shrink in height while the red inner view remains centered within the purple view and maintains its height.
You can also rotate the view too.
What I want is to add an image as a subview, then align it centered along the X axis and 10 points from the bottom of the superview. I need to use Auto Layout only, and preferably visual formatting language.
- (void)viewDidLoad
{
[super viewDidLoad];
self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[self.imageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.imageView setImage:[UIImage imageNamed:#"06-arrow-south"]];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
[self.view addSubview:self.imageView];
[self addConstraints];
self.imageView.layer.borderColor = [[UIColor redColor] CGColor];
self.imageView.layer.borderWidth = 1.0;
}
- (void)addConstraints {
NSDictionary *viewsDictionary = #{#"arrowImage":self.imageView};
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[arrowImage(==40)]-|"
options:0
metrics:nil
views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[arrowImage(==40)]-10-|"
options:0
metrics:nil
views:viewsDictionary]];
}
Here's what I'm getting:
V:|-[arrowImage]-10-|
This aligns the image view so that it is the standard length (20pt) from the top of its superview, and 10 from the bottom. What you want is to PIN it to the bottom only:
V:[arrowImage]-10-|
I'm not sure that centering in the superview can be done with visual format, but you can create a single constraint to center it:
[self.view addConstraint:
[NSLayoutConstraint
constraintWithItem:self.imageView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
There's no need to set the height or width of the image view; its size will be determined from its content.
So, here's the full code for your addConstraints method:
- (void)addConstraints {
[self.view addConstraint:
[NSLayoutConstraint
constraintWithItem:self.imageView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
NSDictionary *viewsDictionary = #{#"arrowImage":self.imageView};
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[arrowImage]-10-|"
options:0
metrics:nil
views:viewsDictionary]];
}
What you currently doing is saying arrowImage should be the full size of the view minus 20px on left, right and top but be minus 10px from bottom.
The to center on x do the following.
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:arrowImage attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
Then as #Austin points out remove the need to be minus 8 from top and be minus 10 from the bottom:
V:[arrowImage]-10-|
Btw its minus 20 as default when you connect a sibling view to a parent: (see comment below )
|-[