XIB view has larger bound values and doesn't fit the screen - ios

I have a XIB view.
When i added it to my view, i want the width and height to fit the iOS device.
However, it doesn't fit.
If i use autolayout it would fit the screen. But how do i use it ?
In the code example below, it takes the Bounds of the XIB file. Therefore it doesn't fit the view. How can i solve this?
-(id)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
self.myView=[[[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil] lastObject];
self.bounds=self.myView.bounds;
[self addSubview:self.myView];
}
return self;
}

Load your view, then
[_subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[_contentView addSubview:_subview];
[self activateConstraintsForView:_subview respectToParentView:_contentView];
Use this function :
- (void)activateConstraintsForView:(UIView *)view respectToParentView:(UIView *)parentView
{
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:parentView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:parentView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:parentView
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:parentView
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0];
[NSLayoutConstraint activateConstraints:[NSArray arrayWithObjects:topConstraint, leftConstraint, bottomConstraint, rightConstraint, nil]] ;
}
Hope, it helps.

In your initWithFrame method add this at the end:
[self setConstraintsOfView:self.myView withRespectToParentView:self];
And add the method below at the end of your class:
-(void)setConstraintsOfView:(UIView *)subView withRespectToParentView:(UIView *)parentView
{
NSDictionary *viewsDictionary = #{#"subView":subView};
NSDictionary *metrics =#{#"offset":#0};
NSArray *heightConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-offset-[subView]-offset-|"
options:0
metrics:metrics
views:viewsDictionary];
NSArray *widthConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-offset-[subView]-offset-|"
options:0
metrics:metrics
views:viewsDictionary];
[parentView addConstraints:heightConstraint];
[parentView addConstraints:widthConstraint];
}
VFL is much easier than setting each constraints. This method sets the top, bottom, leading and trailing space = 0 of subview with respect to superview.
I think you want to achieve this though:
https://github.com/PrajeetShrestha/ConstraintTest

Related

iOS Autolayout relative to the sibling UIViews programatically

I am trying to add 3 custom views (redView, greenView, yellowView) inside a container view (C1) such that all the custom views (redView, greenView, yellowView) are below each other using Auto layout constraints programatically. I want the container view (C1) to get the same size as the size of it subview, so the output should be like this.
The red, green and yellow views are just to show the expected result. Actually the custom view i have is like this.
I am using Auto Layout to do this. Here is my code to do this. RatingsSingleView is my custom view which are shown in the above image.
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIView *ratingsContainerView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *previousTopView = self.ratingsContainerView;
for(int i = 0; i < 3; ++i) {
RatingsSingleView *view = [[RatingsSingleView alloc] init];
view.translatesAutoresizingMaskIntoConstraints = NO;
[self.ratingsContainerView addSubview:view];
NSLayoutConstraint *topConstraint = nil;
if(i == 0) {
// Making the first subview top aligned to the container View top
topConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:previousTopView attribute:NSLayoutAttributeTop multiplier:1.0 constant:10.0];
} else{
// Making the second and third subview top aligned to the view above it
topConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:previousTopView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:10.0];
}
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.ratingsContainerView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:10.0];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.ratingsContainerView attribute:NSLayoutAttributeRight multiplier:1.0 constant:10.0];
[self.ratingsContainerView addConstraint:topConstraint];
[self.ratingsContainerView addConstraint:leftConstraint];
[self.ratingsContainerView addConstraint:rightConstraint];
if(i == 2) {
// Adding last subview bottom to the container View bottom
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.ratingsContainerView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-10.0];
[self.ratingsContainerView addConstraint:bottomConstraint];
}
previousTopView = view;
}
}
#end
So the issue is i am not getting the expected result. I am pinned the container view to the left and right edges and set its height to 0 in the storyboard. once i run the above code i am getting the following result.
Can some body could guide me what i am doing wrong here. thanks
You have given some wrong constraints and I've corrected it try this...
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *previousTopView = self.ratingsContainerView;
for(int i = 0; i < 3; ++i) {
RatingsSingleView *view = [[RatingsSingleView alloc] init];
view.translatesAutoresizingMaskIntoConstraints = NO;
[self.ratingsContainerView addSubview:view];
NSLayoutConstraint *topConstraint = nil;
if(i == 0) {
// Making the first subview top aligned to the container View top
topConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:previousTopView attribute:NSLayoutAttributeTop multiplier:1.0 constant:10.0];
} else{
// Making the second and third subview top aligned to the view above it
topConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:previousTopView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:10.0];
}
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.ratingsContainerView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:10.0];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:self.ratingsContainerView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:10.0];
[self.ratingsContainerView addConstraint:topConstraint];
[self.ratingsContainerView addConstraint:leftConstraint];
[self.ratingsContainerView addConstraint:rightConstraint];
if(i == 2) {
// Adding last subview bottom to the container View bottom
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:self.ratingsContainerView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:10.0];
[self.ratingsContainerView addConstraint:bottomConstraint];
}
previousTopView = view;
}

Autolayout issue with constraintWithItem VS constraintsWithVisualFormat

I have pretty simple UI that works well with constraintsWithVisualFormat, I tried to replace that with constraintWithItem and for some reason it is not working. I don't know what's wrong here.
The literal sentence I understood is
Vertically, the contentView should fill the entire height of its
superview with no padding.
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(contentView);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView]|"
options:0
metrics:nil
views:viewsDictionary];
[containerView addConstraints:constraints];
//Below is my alternative code NOT working, but this should work too?
NSLayoutConstraint *constraints =
[NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:0];
[containerView addConstraint:constraints];
Your new code specifies the height, but not position. The visual format specification says nothing about the item’s height, but instead pins the top and bottom edges of the views to each other.
Instead of creating constraint for height, create two constraints, one for top margin, and one for bottom margin, pinning the edges of contentView to containerView.
You should change it from height to top. And also add similar constraint to bottom.
NSLayoutConstraint *constraints =
[NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
[containerView addConstraint:constraints];
This visual format:
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(contentView);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView]|"
options:0
metrics:nil
views:viewsDictionary];
[containerView addConstraints:constraints];
should be replaced with two constraints:
NSLayoutConstraint *topConstraint =
[NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
[containerView addConstraint: topConstraint];
and
NSLayoutConstraint *bottomConstraint =
[NSLayoutConstraint constraintWithItem:contentView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:containerView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
[containerView addConstraint:bottomConstraint];

How to horizontally center UITextView content in iOS, but not using Align Center?

I want to achieve something very specific. It is hard to describe in words so here are some mockups..
Current stage: I have a UITextView which has autolayout constraints set to be as wide as the parent container.
What I want to achieve: I would like to horizontally center the contents in the UITextView, so that they appear in center but are still left aligned
Most solutions rely on TextAlignCenter option to center the text horizontally, however this is not something that I want as it will appear as such instead:
Any way to achieve this?
I've tried the below:
textview.sizeToFit() seems to adjust the height but not the width
textview.contentOffset does not seem to work
Removing the constraints in autolayout and use Editor > Fix all in scope seems to work on UIButtons but not UITextView..
The reason I want to do this is although it is not noticable on iPhones, on iPads if the text are short they appear all the way to the left, which is very far from my other interactable buttons in my app.
It looks like you are probably using Swift, but that's fine, here's the code for Objective-C, just convert it.
UITextView * _descriptionText = [UITextView new];
[_descriptionText setDelegate:self];
[_descriptionText setBackgroundColor:[UIColor whiteColor];
[_descriptionText setTranslatesAutoresizingMaskIntoConstraints:false];
[_descriptionText setTextColor:[UIColor lightGrayColor]];
[_descriptionText setTextAlignment:NSTextAlignmentLeft];
then, add this textview to a UIView
[sss addSubview:_descriptionText];
Here's the code for the UIView:
UIView * sss = [UIView new];
[sss setTranslatesAutoresizingMaskIntoConstraints:FALSE];
[self addSubview:sss];
Add constraints to the UIView to center the UITextView and to constrain it's bounds by a certain Width on the left and right sides:
[sss addConstraint:[NSLayoutConstraint constraintWithItem:descriptionText attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:sss attribute:NSLayoutAttributeHeight multiplier:1.0f constant:SOME_HEIGHT_NUMBER_YOU_CHOOSE]];
[sss addConstraint:[NSLayoutConstraint constraintWithItem:descriptionText attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:sss attribute:NSLayoutAttributeHeight multiplier:1.0f constant:SOME_RIGHT_MARGIN_NUMBER_YOU_CHOOSE]];
[sss addConstraint:[NSLayoutConstraint constraintWithItem:descriptionText attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:sss attribute:NSLayoutAttributeLeft multiplier:1.0f constant:SOME_LEFT_MARGIN_NUMBER_YOU_CHOOSE]];
or choose a width for the UITextView like this with a CenterX constraint:
[sss addConstraint:[NSLayoutConstraint constraintWithItem:descriptionText attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:SOME_WIDTH_NUMBER_YOU_CHOOSE]];
[sss addConstraint:[NSLayoutConstraint constraintWithItem:descriptionText attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:sss attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0]];
And finally, add a CenterY constraint:
[sss addConstraint:[NSLayoutConstraint constraintWithItem:descriptionText attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:sss attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0]];
Lastly, constrain the UIView "sss" to it's super view "self" or whatever it's superview is:
NSDictionary * views = NSDictionaryOfVariableBindings(sss);
NSDictionary * metrics = #{#"bh" : #30, #"bsh" : #40, ... etc, etc,};
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[sss]|" options:0 metrics:metrics views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-bh-[sss]" options:0 metrics:metrics views:views]];
and then, profit!
Formatted code with subclass UIView
ExampleView.m
#implementation ExampleView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
UIView * sss = [UIView new];
[sss setTranslatesAutoresizingMaskIntoConstraints:false];
[self addSubview:sss];
UITextView * _descriptionText = [UITextView new];
[_descriptionText setBackgroundColor:[UIColor whiteColor]];
[_descriptionText setTranslatesAutoresizingMaskIntoConstraints:false];
[_descriptionText setTextColor:[UIColor lightGrayColor]];
[_descriptionText setTextAlignment:NSTextAlignmentLeft];
[sss addSubview:_descriptionText];
//do these three constraints
[sss addConstraint:[NSLayoutConstraint constraintWithItem:_descriptionText attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:sss attribute:NSLayoutAttributeHeight multiplier:1.0f constant:SOME_HEIGHT_NUMBER_YOU_CHOOSE]];
[sss addConstraint:[NSLayoutConstraint constraintWithItem:_descriptionText attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:sss attribute:NSLayoutAttributeRight multiplier:1.0f constant:SOME_RIGHT_MARGIN_NUMBER_YOU_CHOOSE]];
[sss addConstraint:[NSLayoutConstraint constraintWithItem:_descriptionText attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:sss attribute:NSLayoutAttributeLeft multiplier:1.0f constant:SOME_LEFT_MARGIN_NUMBER_YOU_CHOOSE]];
//or do these three constraints, but don't do all 6 constraints between these three and the three above
[sss addConstraint:[NSLayoutConstraint constraintWithItem:_descriptionText attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:SOME_WIDTH_NUMBER_YOU_CHOOSE]];
[sss addConstraint:[NSLayoutConstraint constraintWithItem:_descriptionText attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:sss attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0]];
[sss addConstraint:[NSLayoutConstraint constraintWithItem:_descriptionText attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:sss attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0]];
NSDictionary * views = NSDictionaryOfVariableBindings(sss);
NSDictionary * metrics = #{#"bh" : #30, #"bsh" : #40};
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[sss]|" options:0 metrics:metrics views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-bh-[sss]" options:0 metrics:metrics views:views]];
// this: V:|-bh-[sss] SHOULD WORK, BUT IT MIGHT REQUIRE YOU TO DO THIS: V:|-bh-[sss(HEIGHT_OF_THE_TEXT_VIEW_THAT_YOU_CHOOSE)]
}
return self;
}
#end
ExampleView.h
#import <UIKit/UIKit.h>
#interface NSHMessagesView : UIView
#end
I think you can subclass UITextView or set contentInset. beacause it's subclass scrollView, so i guess set contentInset can achevie the goal.

Expand views on changing orientation and screen size in auto layout

I have a UIImageView which I need to expand (height and width) on changing orientation and screen size. I am using auto layout constraints for that.
topImageView.contentMode = UIViewContentModeScaleAspectFit;
topImageView.backgroundColor = [UIColor clearColor];
topImageView.layer.cornerRadius = 5.0f;
topImageView.clipsToBounds = YES;
topImageView.translatesAutoresizingMaskIntoConstraints = NO;
if(login_DO.logoPath)
[topImageView loadImage:login_DO.logoPath];
[self.view addSubview:topImageView];
NSArray *horizontalConstraints =
[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:|-(%i)-[topImageView(%f)]",X_OFFSET,VIEW_FRAME_WIDTH-X_OFFSET*2]
options:0 metrics:nil views:#{#"topImageView": topImageView}];
NSArray *verticalConstraints =
[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:|-(%f)-[topImageView(80)]",navHeight]
options:0 metrics:nil views:#{#"topImageView": topImageView}];
[self.view addConstraints:horizontalConstraints];
[self.view addConstraints:verticalConstraints];
NSLayoutConstraint *leadingMarginForImageConstraint = [NSLayoutConstraint
constraintWithItem:topImageView attribute:NSLayoutAttributeLeadingMargin
relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self.view attribute:
NSLayoutAttributeLeadingMargin multiplier:1.0 constant:X_OFFSET];
NSLayoutConstraint *topMarginForImageConstraint = [NSLayoutConstraint
constraintWithItem:topImageView attribute:NSLayoutAttributeTopMargin
relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:self.view attribute:
NSLayoutAttributeTopMargin multiplier:1.0 constant:VIEW_FRAME_WIDTH-X_OFFSET*2];
[self.view addConstraints:#[ leadingMarginForImageConstraint,
topMarginForImageConstraint]];
But the image is not expanding. I am new to auto layouts. Am I missing any constraint?
you can change the imageBottomConstraint from -navHeight to some other value from bottom.
avoid using VIEW_FRAME_WIDTH cause it change when you change orientation.
UIView *superview = self.view;
NSLayoutConstraint *imageTopConstraint = [NSLayoutConstraint
constraintWithItem:topImageView attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual toItem:superview
attribute:NSLayoutAttributeTop multiplier:1.0 constant:navHeight];
NSLayoutConstraint *imageBottomConstraint = [NSLayoutConstraint
constraintWithItem:topImageView attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual toItem:superview
attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-navHeight];
NSLayoutConstraint *imageLeftConstraint = [NSLayoutConstraint
constraintWithItem:topImageView attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual toItem:superview attribute:
NSLayoutAttributeLeft multiplier:1.0 constant:X_OFFSET];
NSLayoutConstraint *imageRightConstraint = [NSLayoutConstraint
constraintWithItem:topImageView attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual toItem:superview attribute:
NSLayoutAttributeRight multiplier:1.0 constant:-X_OFFSET];
[superview addConstraints:#[imageBottomConstraint ,
imageLeftConstraint, imageRightConstraint,
imageTopConstraint]];
for more help check http://www.tutorialspoint.com/ios/ios_auto_layouts.htm
or try using https://github.com/marcoarment/CompactConstraint
let me know if it helped.
I tested the following code that adds an ImageView with Globe.png and add constraints so it appears as you describe. The difference is just to pinch all side edges to the superview (self.view) and then assign the constraints to the superview:
-(void)addImageView{
topImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Globe.png"]]; // Added test image
topImageView.contentMode = UIViewContentModeScaleAspectFit;
topImageView.backgroundColor = [UIColor clearColor];
topImageView.layer.cornerRadius = 5.0f;
topImageView.clipsToBounds = YES;
topImageView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:topImageView];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:topImageView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:topImageView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:topImageView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:topImageView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1.0 constant:0];
[self.view addConstraints:#[topConstraint, bottomConstraint, leftConstraint, rightConstraint]]; //Note constraints are added to the superView
}

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