I'm trying to shift to Auto Layout in my app, but I'm having some trouble with my UITabBarController. Basically, I have two buttons on my home screen, and I want them to have equal sizes, one 50 pixels from the top of the screen and one 50 pixels from the bottom.
The issue is that when I do this:
NSArray* verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-50-
[buttonOne(150)]" options:0 metrics:nil
views:NSDictionaryOfVariableBindings(buttonOne)];
[self.view addConstraints:verticalConstraints];
verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:
[buttonTwo(==buttonOne)]-50-|" options:0 metrics:nil
views:NSDictionaryOfVariableBindings(buttonOne, buttonTwo)];
[self.view addConstraints:verticalConstraints];
The bottom button is 50 pixels from the bottom of the view, which is 568 points tall, when the height of self.view should be the screen height minus the tab bar's height (the navigation bar is hidden). The majority of this padding is eaten up by the height of the tab bar.
My questions is: Why is my view controller's view not resizing so that it doesn't count the space under my tab bar as part of its height?
Suggestion would be to make sure you adding your constraints to a container and not a top level view. ie. Add a view under the top level view and add components to that. My guess is that there are some autoresizing contraints on the top level view
I think railwayparade is correct. I implemented a UITabBar/UITabBarController replacement using auto-layout, it's called GGTabBar. I also wrote a pretty comprehensive Blog Post about how I approached the problem, and how did I use Auto Layout to solve it.
Related
I am using the following code to add a overlay view to a view:
UIView *overlayView = [[UIView alloc] init];
overlayView.backgroundColor = [UIColor redColor];
[self.view addSubview:overlayView];
overlayView.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *views = NSDictionaryOfVariableBindings(overlayView);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[overlayView]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[overlayView]|" options:0 metrics:nil views:views]];
If I place this code in the viewDidLoad method of a UIViewController it works fine. However, if I do the same in a UITableViewController the overlay view gets a zero frame.
I have inspected the view and I can se the constraints do get added correctly and they are active. But for som reason they seem to be ignored.
I don't get any error or warning.
What am I missing here?
PS: I know I can instantiate the overlay view with self.view.bounds as frame, and it works. However, I need to use autolayout.
Suspect it's because the UITableView is a UIScrollView and auto layout has a few caveats when working with views that establish their own bounds systems (such as the scroll view or table view).
The constraints set on the overlay view are such that it is constrained to the top, right, bottom, and left edges of the scroll view, and thus to the content area of the scroll view rather than the scroll view's frame.
See
https://developer.apple.com/library/ios/technotes/tn2154/_index.html
and Auto Layout Guide: Working with Scroll Views:
Constraints between the edges or margins of the scroll view and its
content attach to the scroll view’s content area.
Instead of constraining overlay view to the content area, constrain it to the scroll view's frame instead. According to the above references, this can be done either by constraining overlay view's width/height to the scroll view, or by constraining the overlay view to a view outside of the scroll view.
I have a scrollview and a separate UIView where I placed a series of textFields and labels with constraints which fully occupies the top and bottom. I'm trying to adjust the UIView's height based on its subview constraints but it won't. What is happening is that the view keeps its height and force other textfields to collapse or shrink thus breaking the constraints.
Details
Each subview priority values :
compression = 750
hugging = 250
UIView priority values:
compression = 249
hugging = 749 Set to be lower than the rest.
Most of the textfields has aspect ratio constraint. This causes the field to adjust.
Each subview has vertical/top/bottom spacing between each other. The top and bottom elements has top and bottom constraints to the view as well.
What's on my code:
-(void)viewDidLayoutSubviews{
[super viewDidLayoutSubviews];
/* I had to adjust the UIView's width to fill the entire self.view.*/
if(![contentView isDescendantOfView:detailsScrollView]){
CGRect r = contentView.frame;
r.size.width = self.view.frame.size.width;
contentView.frame = r;
[detailsScrollView addSubview:contentView];
}
}
Screenshots
The view
This is what currently happens. In this instance it forces the email field to shrink. If I place a height value on it, it does not shrink but the layout engine finds another element to break
Edit:
Solved
Maybe I just needed some break to freshen up a bit. I did tried using constraints before but got no luck. However thanks to the suggestion I went back setting the constraints instead of setting the frame on this one and got it finally working.
Solution:
-(void)viewDidLoad{
[detailsScrollView addSubview:contentView];
[contentView setTranslatesAutoresizingMaskIntoConstraints:NO];
[detailsScrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(contentView,detailsScrollView);
NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[contentView]-0-|"
options:NSLayoutFormatDirectionLeadingToTrailing metrics:nil
views:viewsDictionary];
NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[contentView]-0-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:viewsDictionary];
NSArray *widthConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[contentView(==detailsScrollView)]-0-|" options:0 metrics:nil views:viewsDictionary];
}
When you use interface builder to deal with the UIScrollView and its child UIView. usually a top, bottom, left and equal width constraints are set between the UIScrollView and its child which is the contentView in your case.
Without those constraints the other option is to set the content size of the UIScrollView. which was the way of using the UIScrollView before introducing constraints.
So, 1. you should add those constraints programmatically.
By using the constraints, the views frame is no longer needed to resize the views.
So, 2. remove frame setting for your content view.
I am not so happy with the way you set the frame in the viewDidLayoutMethod. if I am going to do that here I would take the frame setting out of the if statement.
The code would be as follow with no if statement:
[detailsScrollView addSubview:contentView];
// then set the constraints here after adding the subview.
Put this code anywhere but not inside your viewDidLayoutSubviews method. it will be a bigger problem than setting the frame in there inside if statement.
Note: Originally, if you are going to set frame in the viewDidLayoutSubviews
method. you should do it for all cases. for example for the if case
and the else case. because, next time this method is going to be
called the views will respond to the constraint. and lose its frame.
Another observation: if you want the view to response to its subviews constraint why you need to set the frame for it? right?
After adding the constraint you may need to call the method constraintNeedsUpdate or another related method.
I am new to Auto layouts, i am trying to design my view with Tableview with Tableview Cell and two Labels. But i am not reach with design similar in iPhone and iPad.
Can any one help to me Please.
Thanks in advance.
You can try:
NSDictionary *views = #{#"label1":self.label1, #"label2":self.label2};
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[label1][label2(==label1)]|"
options:NSLayoutFormatAlignAllCenterY
metrics:nil
views:views]];
The key is [label1][label2(==label1)].
Please add proper constraints to your views. like following
Add pin width and height equal
Leading space for 1st view
Trailing space for 2nd view.
Top/bottom space for both view.
Horizontal/Vertical Spacing
I tried, in interface builder, to position an image between a button and the bottom of the view, and have stay centered in different screen sizes. I could not find a way to do this, so I've tried to accomplish that using the code below, but it's not working. I can get it centered using explicit points, but if use >= it hugs the bottom and all the space is added between the image and button.
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(image, button);
NSArray *constraintsArray = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[button]->=1-[image]->=1-|" options:NSLayoutFormatAlignAllCenterX metrics:nil views:viewsDictionary];
for (int i = 0; i<constraintsArray.count; i++) {
[self.view addConstraint:constraintsArray[i]];
}
How can I get it to center?
Unfortunately, you can't use the >= like that, but it can be done easily in IB. Just give the image view a spacing constraint to the bottom of the superview, and a vertical spacing constraint to the button -- edit one or the other of these to have the same value as the other. Give the image view a fixed height and width constraint, and make sure that the button has no other vertical constraints (delete it/them if it does).
I've got a storyboard which is built using Auto Layout. Within that storyboard, I'm embedding a UIViewController subclass (ButtonGridViewController) in several locations, each of which is a different size. ButtonGridViewController's view is defined in a xib.
What I need is for the entirety of the ButtonGridViewController's view to simply scale-to-fill the view I'm embedding it in. With the old struts-and-springs method, this was trivial -- just set all the subviews to resize in both directions, and voila, piece of cake.
How do I accomplish the same thing using constraints? For what it's worth, the xib just contains a main view, which is rectangular, and has 4 subviews - each a button - arranged in a 2x2 grid. I want everything, including the buttons AND spacing, to scale and/or stretch to fill the view it's going into.
Thanks!
To accomplish the same thing using constraints you need to set the leading, trailing, top and bottom space to the superview to 0. See below:
//load the ButtonGridViewController from a xib
[[NSBundle mainBundle] loadNibNamed:#"..." owner:self options:nil];
//get the view add it
[self.view addSubView:self.myGridView];
//turn off springs and struts
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
//add constraints to fill parent view
NSArray *arr;
//horizontal constraints
arr = [NSLayoutConstraint constraintsWithVisualFormat:#"|[vw]|"
options:0
metrics:nil
views:#{#"vw":self.myGridView}];
[self.view addConstraints:arr];
//vertical constraints
arr = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[vw]|"
options:0
metrics:nil
views:#{#"vw":self.myGridView}];
[self.view addConstraints:arr];