I have a xib file of UIView which has 2 buttons in it. Like this :
Button1 will be either hidden on shown. When it is hidden I want other button to take away whole space.
I also want the view to occupy all available space (orientation change also to be managed). In IB for view, I have selected Size as Freeform, Orientation as Portrait.
For that I added Constraints to both buttons setting 0 to top, bottom and left/right. But with this, when orientation is changed their is gap between both the buttons which I don't want.
I tried many ways but am not able to handle the above two matters. Both are also inter-related. If Button1 is hidden, then should add left constraint to other button also. Right now, I have removed all constraints, so in landscape it doesn't utilize full space.
Can you let me know how do I handle this inter-related issues.
UPDATE
Added the following method to the view (UIView) class. Adding it like this is new for me, with reference have written code to add for top, bottom and left/right setting 0 on both buttons. Left with main role - button1 is hidden then what to add and shown then what.
-(void) addCustomConstriants:(BOOL) hideFirstBtn {
// http://technet.weblineindia.com/mobile/ui-design-of-ios-apps-with-autolayout-using-constraints-programmatically/2/
//NSDictionary *viewsDict = NSDictionaryOfVariableBindings(self.button1, self.button2);
//NSArray *constriaints =
if (hideFirstBtn) {
// ONLY 2ND BUTTON - NO 1ST BUTTON
} else {
// SHOW BOTH BUTTONS
// BUTTON 1
// ADD TOP CONSTRAINT - 0
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.button1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0] ];
// ADD BOTTOM - 0
[self addConstraint: [NSLayoutConstraint constraintWithItem:self.button1 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0 ] ];
// LEFT
[self addConstraint: [NSLayoutConstraint constraintWithItem:self.button1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0 constant:0.0 ] ];
// BUTTON 2
// ADD TOP CONSTRAINT - 0
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.button2 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0] ];
// ADD BOTTOM - 0
[self addConstraint: [NSLayoutConstraint constraintWithItem:self.button2 attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0 ] ];
// ADD RIGHT - 0
[self addConstraint: [NSLayoutConstraint constraintWithItem:self.button2 attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeRight multiplier:1.0 constant:0.0 ] ];
// Width constraint, half of parent view width
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.button1
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:0]];
// Width constraint, half of parent view width
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.button2
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:0.5
constant:0]];
// AFTER ADDING WIDTH ALSO FOR BOTH BUTTONS, WILL HAVE TO ADD X SOME HOW ??? PUZZLE ???
}
}
I believe I will also have to set Width for both the buttons. That should be of equal width. If only button2, then that width also will have to set here only right. How to add constraints for them ?
This is how I call this view from other screen :-
vov1 = [visitorOptView objectAtIndex:0];
// note the origin of the frame is (0, 0) since you are adding it to the cell instead of the table view
vov1.frame = CGRectMake(0, 0, selectedCell.frame.size.width, selectedCell.frame.size.height);
btnWidth = selectedCell.frame.size.width;
[vov1.button2 addTarget:self action:#selector(showVisitorDetailsView) forControlEvents:UIControlEventTouchUpInside];
// show/Enable "Start Chat" btn, else Disable it
if (tableView.tag == 0 || tableView.tag ==1) {
// START
[vov1.button1 setTitle:#"Accept" forState:UIControlStateNormal];
[vov1.button1 addTarget:self action:#selector(acceptBtnClicked) forControlEvents:UIControlEventTouchUpInside];
} else if (tableView.tag == 3) {
// CLOSED LIST - HIDE ALL BTNS
[vov1.button1 setHidden:YES];
}
///// HERE addCustomConstraints SHOULD BE CALLED I THINK
// add overlay view to this row
[selectedCell.contentView addSubview:vov1];
Can you help me with the above.
In that case you should add the constraints by code. So when the two buttons are shown. the first button trailing constraint should be bonded to the leading to the second button constraint. If the "View Details" button is the only one shown then hide the first one and the leading constraint to that button will be bonded with the leading of the self.view .
Create a method called addCustomConstraints and call it in viewDidLoad.
Related
I have a parent VC that loads a child VC inside it. Code in parent VC is as follows:
self.draggerViewController = [[DraggerViewController alloc] initWithSuperView:self.view];
And the code of the child VC is as follows:
- (instancetype)initWithSuperView:(UIView*)superView {
self = [super init];
if (self) {
self.superView = superView;
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
[superView addSubview:self.view];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:superView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeading multiplier:1 constant:0]];
[superView addConstraint:[NSLayoutConstraint constraintWithItem:superView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeTrailing multiplier:1 constant:0]];
self.constraintDraggerAttributeBottom = [NSLayoutConstraint constraintWithItem:superView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:1 constant:0];
[superView addConstraint:self.constraintDraggerAttributeBottom];
}
return self;
}
With this code, the subview is visible on its parent view and constraints are applied correctly so that it is placed at the bottom of the parent view with leading and trailing being 0. BUT, the real frame of the view is outside the parent view.
That is, I can see the subview at the bottom of the parent view but the frame of this subview is frame = (0 -315; 768 35).
If I set 'clipToBounds' to 'YES', the view is placed on the real frame but now constraints are not applied correctly.
How can I place this child VC inside the parent VC's view at the position I want using constraints?
Thank you!
OK, got it...
I was forgetting the height constraint of the view... What a shame...
[superView addConstraint:[NSLayoutConstraint constraintWithItem:superView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1 constant:0]];
Thank you all!
Try this :
superView.insertSubview(self.view, at: self. superView.subviews.count)
I have a modal view that I am adding to the ViewController, now traditionally I have always just resized based on the screen size so that the background of the modal covers the entire screen and has a slight alpha to allow the content to be seen through.
With my current app I am allowing the user to change the orientation so the above method does not work. I was trying to find a way programmatically to assign all the autolayout edges of the modal to 0 in relation to self.view. I have tried the following code but am getting errors.
[self.view addSubview:self.viewPromptSignup];
self.viewPromptSignup.translatesAutoresizingMaskIntoConstraints = NO;
/* pin Left of child to left of parent */
[self.viewPromptSignup addConstraint:[NSLayoutConstraint constraintWithItem:self.view
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.viewPromptSignup
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0]];
/* pin Right of child to right of parent */
[self.viewPromptSignup addConstraint:[NSLayoutConstraint constraintWithItem:self.view
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.viewPromptSignup
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0]];
/* pin top of child to bottom of nav bar(or status bar if no nav bar) */
[self.viewPromptSignup addConstraint:[NSLayoutConstraint constraintWithItem:self.view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.viewPromptSignup
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0]];
/* pin Top of nav bar to bottom of child view */
[self.viewPromptSignup addConstraint:[NSLayoutConstraint constraintWithItem:self.view
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.viewPromptSignup
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0]];
//Creating View
UIView *viewPromptSignup=[UIView new];
viewPromptSignup.translatesAutoresizingMaskIntoConstraints = NO;
[viewPromptSignup setBackgroundColor:[UIColor greenColor]];
//adding to Parent View
[self.view addSubview:viewPromptSignup];
//Top and Bottom Guide
id topGuide = self.topLayoutGuide;
id bottomGuide = self.bottomLayoutGuide;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings (viewPromptSignup, topGuide,bottomGuide);
[self.view addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat: #"V:[topGuide]-10-[viewPromptSignup]"
options: 0
metrics: nil
views: viewsDictionary]
];
[self.view addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat: #"V:[viewPromptSignup]-10-[bottomGuide]"
options: 0
metrics: nil
views: viewsDictionary]
];
// align viewPromptSignup from the left and right
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[viewPromptSignup]-0-|" options:0 metrics:nil views:viewsDictionary]];
You can use Masonry library because it's super easy to add constraints.
Check it out
https://github.com/SnapKit/Masonry
And using this library you can easily set the constraints like this:
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top);
make.left.equalTo(superview.mas_left);
make.bottom.equalTo(superview.mas_bottom);
make.right.equalTo(superview.mas_right);
}];
It's super easy
Is it possible to change autolayout constraints during runtime?
I know you can change the constant, but how would you change different attributes.
For example, NSLayoutAttributeTop to NSLayoutAttributeBottom?
Here is a simple sample of what I hope to achieve, it will set a label top left, then when you press a button it will set the label bottom right.
The initial constraints work as expected, tapping the button doesn't work as expected and throws the infamous "Unable to simultaneously satisfy constraints."
Here is the code I am using:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
self.constraintA = [NSLayoutConstraint constraintWithItem:self.topLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0.0];
self.constraintB = [NSLayoutConstraint constraintWithItem:self.topLabel
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0.0];
[self.view addConstraint:self.constraintA];
[self.view addConstraint:self.constraintB];
}
- (IBAction)tappedChange:(id)sender
{
[self.view removeConstraints:#[ self.constraintA, self.constraintB ]];
self.constraintA = [NSLayoutConstraint constraintWithItem:self.topLabel
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
self.constraintB = [NSLayoutConstraint constraintWithItem:self.topLabel
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0.0];
[self.view addConstraint:self.constraintA];
[self.view addConstraint:self.constraintB];
[self.view setNeedsLayout];
}
Thank you for your time.
You only need to perform the Remove/Recreate/Add constraint dance on iOS 7 and below. If you are writing for a modern iOS (8 and above) you can create all your constraints at once and then just set the .active property on whatever NSLayoutConstraint instance you want at any given time.
// Move to right
self.leadingConstraint.active = false;
self.trailingConstraint.active = true;
// Move to bottom
self.topConstraint.active = false;
self.bottomConstraint.active = true;
If you are using Interface Builder you can create all the constraints that will be needed (note the grayed out constraints that aren't active by default).
Then when the button is pressed you can deactivate the old constraints and activate the new ones.
If you are ever unsure about the views being shown you can pause the app execution and use the following private API in the debugger to print out a full list of views and constraints:
po [[UIWindow keyWindow] _autolayoutTrace]
Personally I like using Masonry to manage constraints in code - it's way less writing, easier to read what you wrote six months ago, and the learning curve isn't as head-splitting either.
For example:
// Define some constraints, making the top one a #property:
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
self.topConstraint = make.top.equalTo(superview.mas_top).with.offset(padding.top);
make.left.equalTo(superview.mas_left).with.offset(padding.left);
}];
// Update the top constraint to match the bottom of the superview instead of the top
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
self.topConstraint = make.top.equalTo(superview.mas_bottom).with.offset(padding.bottom);
}];
I'm creating an ios IPad app that has a UITabBar control positioned at the bottom with 3 MenuItems in it. Normally, when I rotate the device, the tabbar will remain at the bottom in landscape. I'd like to have the TabBar rotate to the left side in Landscape mode, and become a vertical representation. I don't want to create a UIView with buttons if possible, because I'd like the same object to manage both, and I like the convenience of the normal TabBar.
My Plan was to detect the device position, and use a UITransform to rotate the TabBar 90 degrees when in landscape. Let's ignore the icon and text rotation inside for now, and just focus on the overall TabBar itself.
Rotation works, except that it leaves the vertical TabBar in the bottom center of the screen looking very out of place. The next task was to constrain it to the left wall. Normally I am using 3 constraints to hold the TabBar to the Left/Bottom/Right sides. I'm having a lot of trouble finding the right constraints to keep it on the left side correctly.
One main problem is that once the transform is done, height and width attributes are reversed (or also transformed) on the tabbar. So high makes it wider on the screen, and width makes it taller. Using top/bottom constraints along with a height of 49 is what is seemingly required, except the top/bottom +height combo causes a constraint conflict.
I'm attaching my current constraint logic which is reasonably working, yet I can't figure out why I need to hardcode these values as shown. This was the only way to get the visual output on the left side. I'm wondering if there is a cleaner way to do this, perhaps without hardcoded values in there for the constants.
I'm also not sure how I'll end up rotating the text+icons as a next step.
Attached also are how it currently appears. It is green to illustrate clearly but will ultimately be transparent, so the cutoff at the top of landscape won't be a problem.
- (void) adjustViewsForOrientation:(UIInterfaceOrientation) orientation {
switch (orientation)
{
case UIInterfaceOrientationPortrait:
case UIInterfaceOrientationPortraitUpsideDown:
{
//load the portrait view
// revert to normal transform
_tabBar.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI*2);
[self.view removeConstraint:_tabbarConstraintT];
[self.view removeConstraint:_tabbarConstraintR];
[self.view removeConstraint:_tabbarConstraintB];
[self.view removeConstraint:_tabbarConstraintL];
// right space
_tabbarConstraintR = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0];
// bottom space
_tabbarConstraintB = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
// left space
_tabbarConstraintL = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0.0];
//[self.view addConstraint:_tabbarConstraintT];
[self.view addConstraint:_tabbarConstraintR];
[self.view addConstraint:_tabbarConstraintB];
[self.view addConstraint:_tabbarConstraintL];
}
break;
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationLandscapeRight:
{
//load the landscape view
_tabBar.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI/2);
[self.view removeConstraint:_tabbarConstraintT];
[self.view removeConstraint:_tabbarConstraintR];
[self.view removeConstraint:_tabbarConstraintB];
[self.view removeConstraint:_tabbarConstraintL];
NSLog(#"frame: %f,%f",_tabBar.frame.size.height,_tabBar.frame.size.width);
// top space
_tabbarConstraintT = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:[UIScreen mainScreen].bounds.size.height/2+49];
// left space
_tabbarConstraintL = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:-360.0];
// effective width
_tabbarConstraintR = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem: nil
attribute:NSLayoutAttributeHeight
multiplier:1.0f
constant:49];
// effective height
_tabbarConstraintB = [NSLayoutConstraint constraintWithItem:_tabBar
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem: nil
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:[UIScreen mainScreen].bounds.size.height];
[self.view addConstraint:_tabbarConstraintR];
[self.view addConstraint:_tabbarConstraintB];
[self.view addConstraint:_tabbarConstraintT];
[self.view addConstraint:_tabbarConstraintL];
}
break;
case UIInterfaceOrientationUnknown:break;
}
}
I'm using autolayout and i've set the options leading space to superview (constant 0), trailing space to superview (constant 0) and center horizontally to supeview in a table view.
Even though, when coming back from landscape mode, the table view becomes movable you can drag it around i doesn't seem to be attached to the edges of the superview, and it seems that happens because the table view was wider in landscape mode and when it goes back to portrait mode there's some blank room left to the right of the table view to fill the space is not longer ocuppying.
I call this code in every re orientation:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[General addLeftRightConstraintsToView:_detail1TableView inRelationToSuperview:self.navigationController.view];
}
That's a class method i'm using to update layout constraints:
+ (void)addLeftRightConstraintsToView:(UIView *)view inRelationToSuperview:(UIView *)superview
{
// Left Space to Superview
NSLayoutConstraint *leftSpaceConstraint =
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0.0];
// Right Space to Superview
NSLayoutConstraint *rightSpaceConstraint =
[NSLayoutConstraint constraintWithItem:view
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0.0];
[superview addConstraint:leftSpaceConstraint];
[superview addConstraint:rightSpaceConstraint];
}
Any ideas?
This works:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[_details2TableView reloadData];
}