Automatically resized imageview - ios

I have a scrollView and imageView in it.
The app works in landscape and portrait mode.
And i use auto layout.
If i use constant for imageview.
images that I loaded into imageviev it automatically expands under its size.
What i have now:
Landscape mode:
and Portrait mode:
I want to make it as in these pictures
Landscape mode:
and Portrait mode:
How do I fix the auto resize in imageview?
P.S. App for iPad
Thank you all for answers!

Here is an example of code based Autolayout that should help you
#import "UniversalViewController.h"
#interface UniversalViewController ()
#property (strong, nonatomic) UIImageView *myImageView;
#end
#implementation UniversalViewController
#synthesize myImageView;
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
UIImage *myImage = [UIImage imageNamed:#"Velvet_Underground_and_Nico.jpg"];
myImageView = [[UIImageView alloc] initWithImage:myImage];
[myImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:myImageView];
[self setWidth:300 andHeight:300 toView:myImageView];
[self centerView1:myImageView toView2:self.view];
}
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self.view removeConstraints:self.view.constraints];
[self centerView1:myImageView toView2:self.view];
if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight) {
[self setWidth:450 andHeight:250 toView:myImageView];
} else {
[self setWidth:300 andHeight:300 toView:myImageView];
}
}
#pragma mark custom Autolayout methods
- (void) setWidth:(float)width andHeight:(float)height toView:(UIView *)view {
NSLayoutConstraint *myConstraint;
myConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:width];
[self.view addConstraint:myConstraint];
myConstraint = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:height];
[self.view addConstraint:myConstraint];
}
- (void) centerView1:(UIView *)view1 toView2:(UIView *)view2 {
NSLayoutConstraint *myConstraint;
myConstraint = [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
[self.view addConstraint:myConstraint];
myConstraint = [NSLayoutConstraint constraintWithItem:view1 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:view2 attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0];
[self.view addConstraint:myConstraint];
}
You should be able to set the size of the images as you require... I hope it helps...

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

Program Subview's Vertical Spacing to Bottom Constraint

SecondViewController adds a UIView that contains a MKMapView as a subview inside an IBAction method:
if(_tagTwo == 4){
seg2_buttonImg = #"Maps.png";
UIImage *btnImage1 = [UIImage imageNamed:seg2_buttonImg];
[_left_button setImage:btnImage1 forState:UIControlStateNormal];
[_table removeFromSuperview];
[mapVC layoutMapView];
[self.view addSubview:mapVC.view];
return;
}
mapVC is created in ViewDidLoad with mapVC = [[CollectionMapViewController alloc] init];
Edit: new code for adding constraints, taken from #Reinier Melian:
#implementation CollectionMapViewController
#synthesize mapView;
- (void)viewDidLoad
{
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
//[segmentedControl setBackgroundColor:[UIColor whiteColor]];
//[segmentedControl setBackgroundColor:[UIColor leafletBrown]];
[segmentedControl setBackgroundColor:[UIColor leafletLightBrown]];
segmentedControl.layer.cornerRadius = 5;
}
self.mapView.translatesAutoresizingMaskIntoConstraints = NO;
NSArray * verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[mapView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(self.mapView)];
NSArray * horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[mapView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(self.mapView)];
//Add constraints to the Parent
[self.view addConstraints:verticalConstraints];
[self.view addConstraints:horizontalConstraints];
}
CollectionMapViewController.h:
#interface SecondViewController : UIViewController <NSFetchedResultsControllerDelegate, CollectionMapViewControllerDelegate>{
CollectionMapViewController* mapVC;
}
#property (nonatomic, retain) CollectionMapViewController* mapVC;
The problem is that it adds the MKMapView to the top of the screen:
I tried to constrain the MKMapView to the bottom of the screen by adding following code to viewDidLoad of MapViewController
mapView.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *bottom =[NSLayoutConstraint
constraintWithItem:mapView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0f
constant:0.f];
//Add constraints to the Parent
[self.view addConstraint:bottom];
But it just makes the map go black (or more likely moves it completely off the screen?).
What I am trying to get is this:
I would appreciate any help! Thank you so much!
When you add mapView.translatesAutoresizingMaskIntoConstraints = NO; your map rect size is equal to zero if there are not enough constraints to define the correct frame, that is why you have your screen black, you need define all the constraints needed by your MKMapView
Updated using dynamic height constraints
try with this code
self.mapView.translatesAutoresizingMaskIntoConstraints = NO;
CGFloat mapHeigthValue = self.view.frame.size.height/3; //setting the mapView heigth = 1/3 of view height this is an example
NSArray * verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:[mapView(%f)]|",mapHeigthValue] options:0 metrics:nil views:#{#"mapView":self.mapView}];
NSArray * horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[mapView]|" options:0 metrics:nil views:#{#"mapView":self.mapView}];
//Add constraints to the Parent
[self.view addConstraints:verticalConstraints];
[self.view addConstraints:horizontalConstraints];
In viewDidLoad:
mapView.translatesAutoresizingMaskIntoConstraints = NO;
/*Set width and height of mapview */
NSLayoutConstraint *widthConst = [NSLayoutConstraint constraintWithItem:mapView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:400];
NSLayoutConstraint *heightConst = [NSLayoutConstraint constraintWithItem:mapView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:300];
[self.view addConstraint:widthConst];
[self.view addConstraint:heightConst];
/*creater X and Y constraints for mapview */
NSLayoutConstraint *centreHorizontallyConstraint = [NSLayoutConstraint
constraintWithItem:mapView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0];
[self.view addConstraint:centreHorizontallyConstraint];
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint
constraintWithItem:mapView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:140];
[self.view addConstraint:bottomConstraint];

iOS and corona view landscape issue

I am trying to integrate corona (game platform) with iOS project using corona cards. Below is what I am trying to achieve:
Programmatically load corona view controller (ViewController.m/ViewController.h) from AppDelegate.m
Force landscape mode for corona view controller
Below is the code:
AppDelegate.m
ViewController *viewController = [[ViewController alloc] init];
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
ViewController.m
#interface ViewController ()
#property (nonatomic, strong) CoronaViewController *coronaController;
#end
- (void)viewDidAppear:(BOOL)animated {
_coronaController = [[CoronaViewController alloc] init];
[self addChildViewController:_coronaController];
CoronaView *coronaView = (CoronaView *)_coronaController.view;
coronaView.frame = self.view.frame;
[self.view addSubview:coronaView];
[coronaView run];
}
Here's the screenshot of how it looks:
Portrait mode: image
Landscape mode: image
The issue is that when I go to landscape, the image should take up the entire screen but instead it just shows partial image.
* UPDATE *
I was able to get this working by doing the following:
CGRect appFrame = [UIScreen mainScreen].bounds;
_coronaView.frame = CGRectMake(0, -162, appFrame.size.height, appFrame.size.width);
[self.view addSubview:_coronaView];
[_coronaView setNeedsLayout];
However, I am not sure why I had to set my y to -162. Hope this helps.
This happens because when you go to Landscape mode from Portrait mode, the width and height of screen changes. So, coronaView.frame.size.width != [UIScreen mainScreen].bounds.size.width when shifted from Portrait to Landscape
To resolve these issues, it's better to use AutoLayout for setting frame or setting frame in viewDidLayoutSubviews in ViewControllers,
-(void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
CoronaView *coronaView = (CoronaView *)_coronaController.view;
coronaView.frame = self.view.frame;
}
or
- (void)viewDidAppear:(BOOL)animated {
_coronaController = [[CoronaViewController alloc] init];
[self addChildViewController:_coronaController];
CoronaView *coronaView = (CoronaView *)_coronaController.view;
[self.view addSubview:coronaView];
NSLayoutConstraint *constraint1 = [NSLayoutConstraint constraintWithItem:coronaView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
NSLayoutConstraint *constraint2 = [NSLayoutConstraint constraintWithItem:coronaView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeRight multiplier:1 constant:0];
NSLayoutConstraint *constraint3 = [NSLayoutConstraint constraintWithItem:coronaView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTop multiplier:1 constant:0];
NSLayoutConstraint *constraint4 = [NSLayoutConstraint constraintWithItem:coronaView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
[self.view addConstraints:#[constraint1, constraint2, constraint3, constraint4]];
}

How can I resize a UITextView programatically using AutoLayouts?

I have added a UITextView programatically using autolayouts. I want to resize UITextView based on the text size. I have tried some code, but it's not working and I will appreciate it if anybody helps.
my code is below:-
Background class:-
#import "AutoGrowingTextView.h"
#implementation AutoGrowingTextView
- (void) layoutSubviews
{
[super layoutSubviews];
if (!CGSizeEqualToSize(self.bounds.size, [self intrinsicContentSize])) {
[self invalidateIntrinsicContentSize];
}
}
- (CGSize)intrinsicContentSize
{
CGSize intrinsicContentSize = self.contentSize;
// iOS 7.0+
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f) {
intrinsicContentSize.width += (self.textContainerInset.left + self.textContainerInset.right ) / 2.0f;
intrinsicContentSize.height += (self.textContainerInset.top + self.textContainerInset.bottom) / 2.0f;
}
return intrinsicContentSize;
}
#end
my main class
#import "ViewController.h"
#import "AutoGrowingTextView.h"
#interface ViewController ()
{
AutoGrowingTextView * TextView;
}
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
TextView = [AutoTextView new];
TextView.translatesAutoresizingMaskIntoConstraints = NO;
TextView.backgroundColor = [UIColor colorWithRed:0.95 green:0.47 blue:0.48 alpha:1.0];
TextView.scrollEnabled = NO;
[TextView layoutSubviews];
[self.view addSubview:TextView];
NSLayoutConstraint * constraint2 = [NSLayoutConstraint constraintWithItem:TextView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem: self.view attribute:NSLayoutAttributeTop multiplier:1.0f constant:20.0f];
[self.view addConstraint:constraint2];
constraint2 = [NSLayoutConstraint constraintWithItem:TextView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1.0f constant:5.0f];
[self.view addConstraint:constraint2];
constraint2 = [NSLayoutConstraint constraintWithItem:TextView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1.0f constant:-5.0f];
[self.view addConstraint:constraint2];
constraint2 = [NSLayoutConstraint constraintWithItem:TextView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem: nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:50.0f];
[self.view addConstraint:constraint2];
}
#end
This is going to sound a little nuts, but I'm fairly certain that if you remove the height constraint then the layout engine will size the UITextView to fit the font size. I've ran into this same problem just last week and this did the trick.
Remove this constraint, and see if it works:
constraint2 = [NSLayoutConstraint constraintWithItem:TextView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem: nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:50.0f];
Let me add that you should define a font for the textView, that may be the winning ticket for you there on the resize

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