Autolayout stops UITapGestureRecognizer? - ios

I setup a UIImageView to accept tap gesture, which is tapped, the button shall go hidden and shows a UIActivityIndicatorView at the same position. The logic is very simple. but I have two problems:
I need to add either [fb setTranslatesAutoresizingMaskIntoConstraints:NO]; or [loading setTranslatesAutoresizingMaskIntoConstraints:NO]; to avoid constraint conflict. in order to be safe, i added both. seems UIImageView and UIActivityIndicatorView have their own builtin constraints:
"<NSLayoutConstraint:0x8d69e80 UIImageView:0x8d68700.centerX == UIView:0x8d626f0.centerX>",
"<NSLayoutConstraint:0x8d6a0f0 UIActivityIndicatorView:0x8d629b0.centerX == UIView:0x8d626f0.centerX>",
"<NSAutoresizingMaskLayoutConstraint:0x8b48d30 h=--& v=--& UIActivityIndicatorView:0x8d629b0.midX == + 18.5>",
"<NSAutoresizingMaskLayoutConstraint:0x8b495f0 h=--& v=--& UIImageView:0x8d68700.midX == + 60>"
I have to comment off either the line [fb
setTranslatesAutoresizingMaskIntoConstraints:NO]; or [loading
setTranslatesAutoresizingMaskIntoConstraints:NO];. otherwise, the UITapGestureRecognizer doesn't work anymore, why?
code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// setup the loading activity indicator;
loading = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[self.view addSubview:loading];
loading.hidden = YES;
// setup the fb button
fb = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"facebook.png"]];
fb.userInteractionEnabled = YES;
UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] init];
[tap addTarget:self action:#selector(login:)];
[fb addGestureRecognizer:tap];
[self.view addSubview:fb];
fb.hidden = YES;
NSLayoutConstraint *c;
c = [NSLayoutConstraint constraintWithItem:fb
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0];
[self.view addConstraint:c];
c = [NSLayoutConstraint constraintWithItem:fb
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
[self.view addConstraint:c];
c = [NSLayoutConstraint constraintWithItem:loading
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0];
[self.view addConstraint:c];
c = [NSLayoutConstraint constraintWithItem:loading
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
[self.view addConstraint:c];
[fb setTranslatesAutoresizingMaskIntoConstraints:NO];
[loading setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
}

You need to call setTranslatesAutoresizingMaskIntoConstraints:NO because the autoresizing mask gets converted to constraints which conflict with the autolayout system. You usually cannot add auto layout constraints to views with autoresizing masks constraints or you'll get into incompatible constraints.
The main issue in your case is that the width and height of those issues cannot be determined. You need to add width and height constraints as well. The layout can't be determined using the centering constraints if the auto layout system can't calculate them.

Related

Constraints programmatically with Objective C

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];

Why I will get this wrong when I add some constraints in subView?

LogoView is top_bg's subview. top_bg is window's subView. I try to add constraints with LogoView. Why I will get these wrong?
CGRect screenFrame = [[UIScreen mainScreen] bounds];
UIImageView *logoView = [[UIImageView alloc]init];//logo
UIImage *logoImage = [UIImage imageNamed:#"top_ico.png"];
[logoView setImage:logoImage];//below add constraint
// logoView.frame = CGRectMake(150.0f,0.0f, 304.74f, 60.0f);
[logoView setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLayoutConstraint *logoConstraint_0 = [NSLayoutConstraint constraintWithItem:logoView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:top_bg attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f];
NSLayoutConstraint *logoConstraint_1 = [NSLayoutConstraint constraintWithItem:logoView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:top_bg attribute:NSLayoutAttributeLeft multiplier:1.0f constant:screenFrame.size.width/2.0f];
NSLayoutConstraint *logoConstraint_2 = [NSLayoutConstraint constraintWithItem:logoView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:304.74f];
NSLayoutConstraint *logoConstraint_3 = [NSLayoutConstraint constraintWithItem:logoView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:60.0f];
NSArray *LogoConstraints = [NSArray arrayWithObjects:logoConstraint_0,logoConstraint_1,logoConstraint_2,logoConstraint_3,nil];
[top_bg addConstraints:LogoConstraints];
[top_bg addSubview:logoView];
Below is the error when I run it.
2018-03-09 10:43:54.114041+0800 MainUI[1855:81910] [LayoutConstraints] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x60000009b080 UIImageView:0x7f9196429a20.top == UIImageView:0x7f9196615d70.top (inactive)>
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(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] to debug.
I got the answer !,like this code below
-(instancetype) initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if(self){
self.top_bg = [[UIImageView alloc]init];
self.logoView=[[UIImageView alloc]init];
[self.top_bg addSubview:self.logoView];
}
return self;
}
I must be confirm View's hierarchy before I add some constraints ! I can't add constrains then addSubViews.
You need to change the code sequence i.e. first add logoView as subview and then add its constraints.
First add Subview
[top_bg addSubview:logoView];
And the apply constraints
NSArray *LogoConstraints = [NSArray arrayWithObjects:logoConstraint_0,logoConstraint_1,logoConstraint_2,logoConstraint_3,nil];
[top_bg addConstraints:LogoConstraints];

Interaction of View Created in Code with Autolayout

I have a tableview that I create in code. Previously, I assigned it absolute coordinates but as I switch over to autolayout in Storyboard- I don't know how to link it to the element above it.
Basically, the tableView is supposed to be a subview of the container view and it is supposed to be about 12 points below a textField. Can anyone suggest correct way to do this?
This is how I currently create and position the tableview.
_autocompleteTableView = [[UITableView alloc] initWithFrame:
CGRectMake(20, 200, 280, 300) style:UITableViewStylePlain];
[self.view addSubview:_autocompleteTableView];
The constraints in storyboard for the textfield above it are:
textfield.leading = superview.leading+20
textfield.trailing+20 =superview.trailing
textfield.top = elementAbove.bottom+12
textfield.height=30;
Thanks for any suggestions.
Try using anchors. It's super easy to use as simple as constraints in SB.
I think this guide is the best to show how it works. Scroll down to part with title Creating Constraints with Layout Anchors.
Hope it helps!
Try this
Declare
bool once = YES;
then
-(void)viewDidLayoutSubviews
{
if(once)
{
_autocompleteTableView = [[UITableView alloc] initWithFrame:
CGRectMake(20, 200, 280, 300) style:UITableViewStylePlain];
[self.view addSubview:_autocompleteTableView];
self.autocompleteTableView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint* con1=[NSLayoutConstraint constraintWithItem: self.view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.autocompleteTableView attribute:NSLayoutAttributebottom multiplier:1 constant:20];
NSLayoutConstraint* con2=[NSLayoutConstraint constraintWithItem: self.view attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.autocompleteTableView attribute:NSLayoutAttributeLeading multiplier:1 constant:-20];
NSLayoutConstraint* con3=[NSLayoutConstraint constraintWithItem: self.view attribute:NSLayoutAttributeTraling relatedBy:NSLayoutRelationEqual toItem:self.autocompleteTableView attribute:NSLayoutAttributeTraling multiplier:1 constant:20];
NSLayoutConstraint* con4=[NSLayoutConstraint constraintWithItem: self.autocompleteTableView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.textfield attribute:NSLayoutAttributeBottom multiplier:1 constant:12];
[self.view addConstraints:#[con1,con2,con3,con4]];
[self.view layoutIfNeeded];
once = NO;
}
}

Centre the button in view use constraints programmatically in iOS 8

I have a subview and added 1 button. I want to add this button in the center of that view. I use Autolayout so i need to set the constraints for this button programmatically.
I tried this code but the play button is not in the center.
[self.moviePlayer.view addSubview:self.playbtn];
self.playbtn.translatesAutoresizingMaskIntoConstraints = NO;
[self.moviePlayer.view addConstraint:[NSLayoutConstraint constraintWithItem:self.playbtn attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual
toItem:self.moviePlayer.view attribute:NSLayoutAttributeCenterX multiplier:0.667 constant:0]];
Please help me to correct it. Thanks in advance.
You can use the NSLayoutAttributeCenterX and NSLayoutAttributeCenterY attributes to center your play button like this:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initViews];
[self initConstraints];
}
-(void)initViews
{
self.picture = [[UIImageView alloc] init];
self.picture.image = [UIImage imageNamed:#"bg"];
self.playButton = [[UIButton alloc] init];
[self.playButton setImage:[UIImage imageNamed:#"playButton"] forState:UIControlStateNormal];
[self.view addSubview:self.picture];
[self.view addSubview:self.playButton];
}
-(void)initConstraints
{
self.picture.translatesAutoresizingMaskIntoConstraints = NO;
self.playButton.translatesAutoresizingMaskIntoConstraints = NO;
id views = #{
#"picture": self.picture,
#"playButton": self.playButton
};
// picture constraints
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[picture]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[picture]|" options:0 metrics:nil views:views]];
// play button constraints
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.playButton attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.playButton attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
}
You get something like this:
You can use this code to center any item (es. self.btn) in its superview:
[self.btn.superview addConstraint:[NSLayoutConstraint
constraintWithItem:self.btn.superview
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.btn
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0]];
[self.btn.superview addConstraint:[NSLayoutConstraint
constraintWithItem:self.btn.superview
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.btn
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0]];
I also wrote an article about aligning any UIView objects without using Storyboards on my blog.

Autolayout issue

I am developing an application in which i am using auto layout. I am following the following steps :
step 1 : create a button in viewDidLoad
[super viewDidLoad];
self.view.translatesAutoresizingMaskIntoConstraints = NO;
_button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_button1.translatesAutoresizingMaskIntoConstraints = NO;
[_button1 setTitle:#"B" forState:UIControlStateNormal];
[self.view addSubview:_button1];
step 2 : implement constraints in updateViewConstraints method
[super updateViewConstraints];
[self.view removeConstraints:self.view.constraints];
if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation))
{
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0f constant:100.0f];
[self.view addConstraint:constraint];
NSLayoutConstraint *constraint1 = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0f constant:-100.0f];
[self.view addConstraint:constraint1];
NSLayoutConstraint *constraint2 = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0f constant:200.0f];
[self.view addConstraint:constraint2];
NSLayoutConstraint *constraint3 = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0f constant:-100.0f];
[self.view addConstraint:constraint3];
_button1.backgroundColor = [UIColor redColor];
} else{
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0f constant:200.0f];
[self.view addConstraint:constraint];
NSLayoutConstraint *constraint1 = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0f constant:-200];
[self.view addConstraint:constraint1];
NSLayoutConstraint *constraint2 = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0f constant:50.0f];
[self.view addConstraint:constraint2];
NSLayoutConstraint *constraint3 = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0f constant:-50.0f];
[self.view addConstraint:constraint3];
_button1.backgroundColor = [UIColor blueColor];
}
but when i switch the device orientation, the console prints the following :
Unable to simultaneously satisfy constraints. Probably at least one
of the constraints in the following list is one you don't want. Try
this: (1) look at each constraint and try to figure out which you
don't expect; (2) find the code that added the unwanted constraint or
constraints and fix it. (Note: If you're seeing
NSAutoresizingMaskLayoutConstraints that you don't understand, refer
to the documentation for the UIView property
translatesAutoresizingMaskIntoConstraints) (
"UIView:0x8a461c0 (Names: '|':UIWindow:0x8a42970 )>",
"",
"",
"UIButton:0x8a45ea0 (Names: '|':UIView:0x8a461c0 )>",
"" )
Will attempt to recover by breaking constraint
Break on objc_exception_throw to catch this in the debugger. The
methods in the UIConstraintBasedLayoutDebugging category on UIView
listed in may also be helpful.
could anyone please tell me what is wrong with this layout ?
The issue is that you're calling [super updateViewConstraints] in updateViewConstraints while you still have constraints in place for the button. So, as you transition from landscape to portrait, you still have the landscape button constraints (which are unsatisfiable in portrait), but are asking the main view to update its (portrait) constraints. If you move the call to [super updateViewConstraints] anywhere after you remove all of your existing button constraints, and you should be in good shape.
A couple of asides:
If using storyboards/NIBS, you should remove the line that says:
self.view.translatesAutoresizingMaskIntoConstraints = NO;
But keep the line that says:
_button1.translatesAutoresizingMaskIntoConstraints = NO;
I'd be wary of a wholesale removal of all constraints. I usually keep arrays of the constraints I want to remove, and that way I can easily remove just the ones that I need removing and will be reconstructing. In your case, removing all is probably fine, but as you add more and more constraints to your view, it's probably just easier to keep track of which you want to remove and reconstruct:
#property (nonatomic, strong) NSArray *verticalConstraints;
#property (nonatomic, strong) NSArray *horizontalConstraints;
I might suggest using VFL, which is a little more concise:
- (void)updateViewConstraints
{
if (self.horizontalConstraints)
[self.view removeConstraints:self.horizontalConstraints];
if (self.verticalConstraints)
[self.view removeConstraints:self.verticalConstraints];
[super updateViewConstraints];
NSDictionary *views = NSDictionaryOfVariableBindings(_button1);
NSDictionary *metrics = nil;
if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation))
{
metrics = #{#"left" : #100,
#"right" : #100,
#"top" : #200,
#"bottom" : #100};
_button1.backgroundColor = [UIColor redColor];
} else{
metrics = #{#"left" : #200,
#"right" : #200,
#"top" : #50,
#"bottom" : #50};
_button1.backgroundColor = [UIColor blueColor];
}
self.horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(left)-[_button1]-(right)-|" options:0 metrics:metrics views:views];
self.verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(top)-[_button1]-(bottom)-|" options:0 metrics:metrics views:views];
[self.view addConstraints:self.horizontalConstraints];
[self.view addConstraints:self.verticalConstraints];
}
This can also be done without checking on the orientation by using both the multiplier and constant values of the constraint to create a single constraint (for each direction) that works for both portrait and landscape (If you make the view in the storyboard, you need to remove any constraints you made there before adding these -- you can have that done automatically by checking the "Placeholder - Remove at build time" box in the attributes inspector for each of the constraints you want removed). In you particular case, I think these values work:
- (void)viewDidLoad {
[super viewDidLoad];
_button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_button1.translatesAutoresizingMaskIntoConstraints = NO;
[_button1 setTitle:#"B" forState:UIControlStateNormal];
[self.view addSubview:_button1];
NSLayoutConstraint *topCon = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeTop relatedBy:0 toItem:self.view attribute:NSLayoutAttributeBottom multiplier:.9375 constant:-250];
NSLayoutConstraint *bottomCon = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeBottom relatedBy:0 toItem:self.view attribute:NSLayoutAttributeBottom multiplier:.6875 constant:50];
NSLayoutConstraint *leftCon = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeLeft relatedBy:0 toItem:self.view attribute:NSLayoutAttributeRight multiplier:.625 constant:-100];
NSLayoutConstraint *rightCon = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeRight relatedBy:0 toItem:self.view attribute:NSLayoutAttributeRight multiplier:.375 constant:100];
[self.view addConstraints:#[topCon,bottomCon,rightCon,leftCon]];
}
Notice that the attribute for self.view is bottom for the top constraint, and right for the left constraint. When using the multiplier, you have to do it this way, since the left and top attribute values are zero, so multiplying by anything would be useless.
Calculating these values by hand is a pain, so I don't actually set them up that way. Instead, I've written a category on NSLayoutConstraint that allows me to set up the constraints like this (an exampleProject with the category can be found at http://jmp.sh/v/fgHhRNX2twlrgG338CDz):
[self.view addConstraint:[NSLayoutConstraint rightConstraintForView:_button1 viewAttribute:NSLayoutAttributeRight superview:self.view portraitValue:100 landscapeValue:200]];
[self.view addConstraint:[NSLayoutConstraint topConstraintForView:_button1 viewAttribute:NSLayoutAttributeTop superview:self.view portraitValue:200 landscapeValue:50]];
[self.view addConstraint:[NSLayoutConstraint bottomConstraintForView:_button1 viewAttribute:NSLayoutAttributeBottom superview:self.view portraitValue:100 landscapeValue:50]];
[self.view addConstraint:[NSLayoutConstraint leftConstraintForView:_button1 viewAttribute:NSLayoutAttributeLeft superview:self.view portraitValue:100 landscapeValue:200]];
Typically layout constraints are built in IB and then adjusted on orientation change, not discarding and recreating constraints on orientation change as you seem to want to do.
Anyway, the problem looks to be that you are not removing all the required constraints. the line [self.view removeConstraints:self.view.constraints]; only removes constraints the views own constraints and ignores the fact that there are probably constraints on other views (i.e. the superview) relating to view.
I don't know for sure if this is your problem, but I would try and adjust existing constraints instead and see if that fixes the problem. You can make IBOutlets for layout constraints if that will help you.
I copy & pasted your stuff into a completely fresh project and it works fine. So you probably have something more in your project which might interfere. Are you using Storyboards?
#import "DemoViewController.h"
#interface DemoViewController()
#property (nonatomic, strong) UIButton *button1;
#end
#implementation DemoViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.translatesAutoresizingMaskIntoConstraints = NO;
_button1 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
_button1.translatesAutoresizingMaskIntoConstraints = NO;
[_button1 setTitle:#"B" forState:UIControlStateNormal];
[self.view addSubview:_button1];
}
- (void)updateViewConstraints
{
[super updateViewConstraints];
[self.view removeConstraints:self.view.constraints];
if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation))
{
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0f constant:100.0f];
[self.view addConstraint:constraint];
NSLayoutConstraint *constraint1 = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0f constant:-100.0f];
[self.view addConstraint:constraint1];
NSLayoutConstraint *constraint2 = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0f constant:200.0f];
[self.view addConstraint:constraint2];
NSLayoutConstraint *constraint3 = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0f constant:-100.0f];
[self.view addConstraint:constraint3];
_button1.backgroundColor = [UIColor redColor];
} else{
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0f constant:200.0f];
[self.view addConstraint:constraint];
NSLayoutConstraint *constraint1 = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0f constant:-200];
[self.view addConstraint:constraint1];
NSLayoutConstraint *constraint2 = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0f constant:50.0f];
[self.view addConstraint:constraint2];
NSLayoutConstraint *constraint3 = [NSLayoutConstraint constraintWithItem:_button1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0f constant:-50.0f];
[self.view addConstraint:constraint3];
_button1.backgroundColor = [UIColor blueColor];
}
}
#end
and the AppDelegate:
#import "AppDelegate.h"
#import "DemoViewController.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = [[DemoViewController alloc] init];
[self.window makeKeyAndVisible];
return YES;
}
#end
If you remove this line
self.view.translatesAutoresizingMaskIntoConstraints = NO;
Then i believe you should not longer have the issue, i have seen it a few times where if you use the storyboards then adding this line of code will cause these types of issues to appear when using the application.

Resources