Autolayout - adding and running constraint - ios

I have a view with 2 container views: one main one on top and one at the bottom.
When the app launches, the bottom one is hidden via a frame that goes beyond the screen height. The top one in the meantime occupies the entire app window.
When I decide to show that bottom container, I want the top container to decrease in height and the view of the controller in that main container to be impacted as well.
I tried to add a constraint programmatically and used layoutIfNeeded but nothing worked.
I'm new to this. I don't necessarily want the best answer but how I should approach this.
Thanks!!!!
-(void)showBottom {
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.bottomContainer attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.mainContainer attribute:NSLayoutAttributeTop multiplier:1.0f constant:49.0f];
[self.view addConstraint:constraint];
}

You can try pinning objects with a Top Space to Superview constraint and animating it.
// .h
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;
// .m
[UIView animateWithDuration:1.0 animations:^{
self.topConstraint.constant = 0;
[self.nView layoutIfNeeded];
}];

Related

How do I animate constraint changes if constraints are created programmatically with VFL?

I have trouble on something, I would be grateful if you could give me some advice.
Basically, I created some constraints with Visual Format Language and makes my base more dynamic. I didn't touch my views on storyboard.
You can consider red area as a navigationBar.
In example view looks like;
My problem is; I want to hide my topView on some viewController.
First I created a property;
#property (strong, nonatomic) NSLayoutConstraint *constraintTabbarHeight;
But I realized I couldn't set my NSLayoutConstraint property with VFL. Because VFL is create an array of constraints.
So this is my first question;
Is there anyway to keep my VFL constraint with NSLayoutConstraint property?
Anyway, so I decided to create my height constraint with constraintWithItem method.
self.constraintTabbarHeight = [NSLayoutConstraint constraintWithItem:self.tabbarContainerView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:95];
Second, I search on stackoverflow and I have found a method. I can call this method from any viewController I want.
- (void)updateTabbarConstraintWith:(int) heightValue{
self.constraintTabbarHeight.constant = heighValue;
[UIView animateWithDuration:0.3
animations:^{
[self.view layoutIfNeeded];
}];
}
If I set self.constraintTabbarHeight.constant = 0 ,everything is fine. My TopView (redArea) is disappearing but PlaceholderView is still on same position.
I thought [self.view layoutIfNeeded] will update my placeholder View and it fills my screen.
It looks like this;
How do I make it correct?
Thanks for your answers.
You need to create a constraint from your placeholderView.Top to your TopView.Bottom, otherwise, one will be able to move without impacting the other one

move button towards target using constraints, no reaction?

I would like the red button to be animated towards the leading position of the second button :
Some examples showed how to change the "constant" with numbers, but I would like to put automatically at the leading position of the second button.
I tried this, but the red button does not move, the animations log is correctly called though :
- (void)updateConstr{
NSLayoutConstraint *newLeading = [NSLayoutConstraint
constraintWithItem:self.redB
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.secondButton
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0.0f];
self.leadingConstraint = newLeading;//is an IBOutlet pointing on the constraint (see the image)
[self.redB setNeedsUpdateConstraints];
[UIView animateWithDuration:0.5 animations:^{
[self.redB layoutIfNeeded];
NSLog(#"animations");//is called, but the red button does not move
}];
}
- (IBAction)firstAction:(id)sender { //after a click on button "one"
NSLog(#"firstAction");
[self updateConstr];
}
This must do it:
- (void)updateConstr{
NSLayoutConstraint *newLeading = [NSLayoutConstraint
constraintWithItem:self.redB
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.secondButton
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0.0f];
[self.redB.superview removeConstraint: self.leadingConstraint];
[self.redB.superview addConstraint: newLeading];
self.leadingConstraint = newLeading;
[UIView animateWithDuration:0.5 animations:^{
[self.redB layoutIfNeeded];
}];
}
What I typically do in this situation is the following.
Add two constraints to your scene. One where it's aligned left between the button and the "one" label. The second, where it's aligned left between the button and the "second" label (i.e. both values will be 0). These constraints will initially conflict with one another, and that's fine.
Add IBOutlets to your view controller for the NSLayoutConstraints and assign the two constraints we've created to those IBOutlets.
Set the constraint priority on your initial condition to 999 (i.e. constraint on left align to "one" should be 999). Set the constraint priority on the destination constraint to 998 (i.e. constraint on left align between button to "second" is 998). You'll now see that these constraints will no longer conflict. This is because the priority on one constraint overrides the other.
You may see where this is headed now. So when you want to animate the button between the constraints, swap the priorities and animate!
Code:
#interface MyViewController ()
#property (nonatomic, weak) NSLayoutConstraint* constraint0;
#property (nonatomic, weak) NSLayoutConstraint* constraint1;
#end
- (void)someMethodWhereIWantToAnimate
{
NSInteger temp = constraint0.priority;
constraint0.priority = constraint1.priority;
constraint1.priority = temp;
[UIView animateWithDuration:1.0 animations:^{
// Simplest is to use the main ViewController's view
[self.view layoutIfNeeded];
}];
}

Resize superview while adding subviews dynamically with autolayout

I have to show a popover on iPhone screen with multiple "Switch" controls. And to add and remove subviews on/from popover view with switch on/off actions respectively. For better illustration of the situation see below
images.
The above popover view first appears when user taps on a button. The popover has to stay always at the center of the screen and initially add contact switch will be in off condition. When turned on the below subviews has to be added on popover while keeping the popover in center of the screen and increasing the height of popover as per subviews.
And just like the above the popover view has to grow again in height with adding two more subviews when "Add mail" switch will be "ON". And finally look like this,
That's it. I am using auto-layout through out my application and this is where I am perplexed. I know I can remove the popovers and one more new each time but that seems to be kind of novice option. So is there any simple way to add subviews and expand its superview dynamically with auto-layout ? I've seen many questions with UILabel and working with respect to it's intrinsic content size but still unable to get any idea with this particular situation. Any help will be appreciated. Happy coding.
This can be accomplished with plain layout constraints without having to manually constrain the height of the container view, and then update the constant of that constraint.
The way to do this, is to constrain the container view's height based on the bottom of the bottom most subview.
Then put a reference to this constraint within your view controller.
now you can write something like the following view controller, which will add a new subview at the bottom of the container view, and automatically update the container view's height.
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomConstraint;
#property (weak, nonatomic) IBOutlet UIButton *addButton;
#property (weak, nonatomic) IBOutlet UIView *containerView;
#property (nonatomic, weak) UIView *lastView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.lastView = self.addButton;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)addButtonTapped:(id)sender {
UIView *newView = [[UIView alloc] initWithFrame:CGRectZero];
newView.translatesAutoresizingMaskIntoConstraints = NO;
newView.backgroundColor = [UIColor redColor];
[newView addConstraint:[NSLayoutConstraint constraintWithItem:newView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:35]];
[self.containerView addSubview:newView];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[lastView]-(14)-[newView]"
options:NSLayoutFormatAlignAllCenterX
metrics:nil
views:#{#"lastView" : self.lastView, #"newView" : newView}]];
[self.containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(10)-[newView]-(10)-|"
options:NSLayoutFormatAlignmentMask
metrics:nil
views:#{#"newView":newView}]];
[self.containerView removeConstraint:self.bottomConstraint];
self.bottomConstraint = [NSLayoutConstraint constraintWithItem:self.containerView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:newView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:14];
[self.containerView addConstraint:self.bottomConstraint];
self.lastView = newView;
}
#end
Add this all together, and you should get the following behavior.
You can outlet height constraint of the view, and then set value accordingly to elements.

IBOutlet didn't work with Hard Code constraints

I have new issue related to Auto-Layout world , i can summarize the problem in the below steps:
1- I have storyboard with only one scene contain UIImageView .
2- I went to viewcontroller.m file and add manual constraints like below code
[self.bgImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
NSLayoutConstraint *horizentalSpaceConstraint = [NSLayoutConstraint constraintWithItem:self.bgImageView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
[self.view addConstraint:horizentalSpaceConstraint];
Result :
The constraint didn't affect the IBoutlet for UIImageView but if i add UIImageView from hard coded in viewcontroller.m file it works ,can you help me to discover this problem.
I found the solution which can describe by ( Every constraints used IBOutlet elements should before begin add only the below line without need to [setTranslatesAutoresizingMaskIntoConstraints :No]
-(void)viewWillAppear:(BOOL)animated{
// Step 1 remove view constraints for IBOutlet elements
[self.view removeConstraints:self.view.constraints];
}

iOS two views cover exactly half of parent view

In my app I want to achieve this layout:
So parent view contains two sub views. First one ends exactly in a middle (height / 2) and second starts in a middle of parent view. I have found out that it is impossible to do that in the IB with constraints. So I used this code in viewDidLoad method:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:firstView
attribute:NSLayoutAttributeHeight
relatedBy:0
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
Now it works but only if the app runs on the iPhone. Because size of the view is like iPhone screen. If this app runs on the iPad, there is a problem because screen has different size so this parent view is longer. And constraint (code above) still takes 0.5 * size of the views size from the IB and not size from the iPad size of the view. Item toItem:self.view still takes size from the IB.
Result is that this view has a same size in the iPad as in the iPhone. In the iPad there is a large blank space and then there is a view with iPhone size.
Can you tell what I have to do to make it universal for various screen sizes? Thank you very much
This is possible using constraints, but it is made a bit fiddly by IBs rather annoying and inflexible constraint manager. Here is how I managed it:
In IB, set the two views with the correct frames
Add an equal height constraint between the two views
Reduce the priority of any default height constraints on either of the views. Unfortunately IB does not let you remove these entirely, but setting them to anything less than 1000 will make sure they are ignored.
In the view controllers viewDidLoad method, add the constraint you already tried.
eg
- (void)viewDidLoad
{
[super viewDidLoad];
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.topView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
}
Thats it. Screengrabs of the IB constraints are shown below:
Try this code . It will set constraint value dynamically
In your .h file , implement this lines.
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *TopsubviewheightConstraint;
Now create this constraint's as per given screen shot
connect TopsubviewheightConstraint height constraint from screen
implement this code in .m file
if (IS_IPHONE_5)
_TopSuperViewConstraint.constant = 275;
else if(IS_IPAD)
_TopSuperViewConstraint.constant = 502;
else
_TopSuperViewConstraint.constant = 230;
I hope it will help you.
you have 2 options.
create a second IB file for iPad
do everything by programm and use [[UIScreen mainScreen] bound]; instead of getting the sizes of parent ;)
I would do it without the constraints at all and set as follow:
// self.view is my container view
CGRect frame = [[UIScreen mainScreen] bound];
frame.size.height /= 2;
// upper View
upperView.frame = frame;
// lower View
frame.origin.y = frame.size.height;
// or alternatively
//frame.origin.y = CGRectGetMaxY(frame);
lowerView.frame = frame;
here you don't need any device specific options, everything is dynamic, bound to the size of your device's screen ;)
OK so I just figured out how to do this. Simply put the code into viewDidLayoutSubviews method and not to viewDidLoad. The solution I found in the topic Unable to set frame correctly before viewDidAppear.
Here is my code:
[subView1 setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height / 2)];
[subView2 setFrame:CGRectMake(0, self.view.frame.size.height / 2, self.view.frame.size.width, self.view.frame.size.height / 2)];
Thanks to all for effort!
Thanks to Tark's answer, I managed to to this using constraints as well:
Add Vertical Space constraint for TobView to Top Layout Guide (Using StoryBoard)
Add Vertical Space constraint for BottomView to Bottom Layout Guide (Using StoryBoard)
Add two height constraints for each view in ViewDidLoad
Code:
NSLayoutConstraint *constraint;
constraint = [NSLayoutConstraint constraintWithItem:_viewTop
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:_viewBottom
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
You can do this with constrains (no code required!)
1.- First create two UIview and manually set it's height to half of the size of the current device, positioning one over the other, just like this:
2.- Next you must set the constraints for each one of them like this (this will allow to the container fill the whole screen, one over the another):
Top container
Bottom container
3.- Finally you must select both containers and add a new constrain that specify that they will have in the same height
(remember to clid "Add X Constrains" for each step)
now it should be ready to put the label inside each container, and you will ready

Resources