I have a view controller with 12 UITextFields.
It fits a 3.5" display very well.
I need to set it for iPhone 5 (4 inch display) such that all UITextFields cover the whole UIView by adding extra space in between.
I am trying to do this by auto layout but it is not working properly.
This is my code :
- (void) viewWillLayoutSubviews
{
int h = txt1.bounds.size.height * 12;
float unusedHorizontalSpace = self.view.bounds.size.height - h ;
NSNumber* spaceBetweenEachButton= [NSNumber numberWithFloat: unusedHorizontalSpace / 13 ] ;
NSMutableArray *constraintsForButtons = [[NSMutableArray alloc] init];
[constraintsForButtons addObjectsFromArray: [NSLayoutConstraint constraintsWithVisualFormat: #"V:|-50-[txt1(==30)]-(space)-[txt2(==txt1)]-(space)-[txt3(==txt1)]-(space)-[txt4(==txt1)]-(space)-[txt5(==txt1)]-(space)-[txt6(==txt1)]-(space)-[txt7(==txt1)]-(space)-[txt8(==txt1)]-(space)-[txt9(==txt1)]-(space)-[txt10(==txt1)]-(space)-[txt11(==txt1)]-(space)-[txt12]-(space)-|"
options: NSLayoutFormatAlignAllCenterX
metrics: #{#"space":spaceBetweenEachButton}
views: NSDictionaryOfVariableBindings(txt1,txt10,txt11,txt12,txt2,txt3,txt4,txt5,txt6, txt7,txt8,txt9)]];
[self.view addConstraints:constraintsForButtons];
}
If I do [txt12(==txt1)] then it displays the same as the 3.5" screen and leaves space below.
Where I am making a mistake?
To do this with auto layout, you must create extra views to fill the spaces between the text fields.
Recall that an auto layout constraint is basically the linear equation A = m * B + c. A is an attribute of one view (for example, the Y coordinate of viewA's bottom edge) and B is an attribute of another view (for example, the Y coordinate of viewB's top edge). m and c are constants. So, for example, to lay out viewA and viewB so that there are 30 points between the bottom of viewA and the top of viewB, we could create a constraint where m is 1 and c is -30.
The problem you're having is that you want to use the same value for c across 13 different constraints, and you want auto layout to compute that c value for you. Auto layout simply can't do that. Not directly. Auto layout can only compute the attributes of views; it cannot compute the m and c constants.
There is a way to make auto layout put the views where you want: reify the spaces between the text fields as additional (invisible) views. Here's an example with just 3 text fields:
We'll create a constraint to pin each spacer's top edge to the bottom edge of the text field above it. We'll also create a constraint to pin each spacer's bottom edge to the top edge of the text field below it. And finally, we'll create a constraint to force each spacer to have the same height as the topmost spacer.
We'll need a two instance variables to set things up: an array of the text fields (in order from top to bottom), and a reference to the topmost spacer view:
#implementation ViewController {
NSMutableArray *textFields;
UIView *topSpacer;
}
We'll create the text fields and spacers in code since it's hard to show a xib in a stackoverflow answer. We kick things off in viewDidLoad:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.translatesAutoresizingMaskIntoConstraints = NO;
[self addTextFields];
[self addSpacers];
}
Since we're going to use auto layout, we need to turn off translatesAutoresizingMaskIntoConstraints to prevent the system from creating extra constraints.
We create each text field, give it some dummy text, and set up constraints for its horizontal position and size:
- (void)addTextFields {
textFields = [NSMutableArray array];
for (int i = 0; i < 12; ++i) {
[self addTextField];
}
}
- (void)addTextField {
UITextField *field = [[UITextField alloc] init];
field.backgroundColor = [UIColor colorWithHue:0.8 saturation:0.1 brightness:0.9 alpha:1];
field.translatesAutoresizingMaskIntoConstraints = NO;
field.text = [field description];
[self.view addSubview:field];
[field setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[field setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-[field]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(field)]];
[textFields addObject:field];
}
We'll use a loop to create the spacers too, but we create the top and bottom spacers differently from the middle spacers, because we need to pin the top and bottom spacers to the superview:
- (void)addSpacers {
[self addTopSpacer];
for (int i = 1, count = textFields.count; i < count; ++i) {
[self addSpacerFromBottomOfView:textFields[i - 1]
toTopOfView:textFields[i]];
}
[self addBottomSpacer];
}
Here's how we create the top spacer and set up its constraints. Its top edge is pinned to the superview and its bottom edge is pinned to the first (topmost) text field. We store the top spacer in the instance variable topSpacer so we can constrain the other spacers to have the same height as the top spacer.
- (void)addTopSpacer {
UIView *spacer = [self newSpacer];
UITextField *field = textFields[0];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[spacer][field]" options:0 metrics:nil
views:NSDictionaryOfVariableBindings(spacer, field)]];
topSpacer = spacer;
}
Here's how we actually create a spacer view. It's just a hidden view. Since we don't care about its horizontal size or position, we just pin it to the left and right edges of the superview.
- (UIView *)newSpacer {
UIView *spacer = [[UIView alloc] init];
spacer.hidden = YES; // Views participate in layout even when hidden.
spacer.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:spacer];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"|[spacer]|" options:0 metrics:nil
views:NSDictionaryOfVariableBindings(spacer)]];
return spacer;
}
To create a “middle” spacer between two text views, we pin it to the bottom edge of the text
field above and the top edge of the text field below. We also constrain its height to equal the height of the top spacer.
- (void)addSpacerFromBottomOfView:(UIView *)overView toTopOfView:(UIView *)underView {
UIView *spacer = [self newSpacer];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:[overView][spacer(==topSpacer)][underView]" options:0 metrics:nil
views:NSDictionaryOfVariableBindings(spacer, overView, underView, topSpacer)]];
}
To create the bottom spacer, we pin it to the last text field and to the superview. We also constrain its height to equal the height of the top spacer.
- (void)addBottomSpacer {
UIView *spacer = [self newSpacer];
UITextField *field = textFields.lastObject;
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:[field][spacer(==topSpacer)]|" options:0 metrics:nil
views:NSDictionaryOfVariableBindings(spacer, field, topSpacer)]];
}
If you do it right, you will get a result like this:
You can find a complete example project in this github repository.
Check out PureLayout. It's designed to be the simplest and most programmer-friendly API possible for creating Auto Layout constraints in code.
In response to your specific question, PureLayout offers a two primary APIs for distributing views, one where the spacing between each view is fixed (view size varies as needed), and the other where the size of each view is fixed (spacing between views varies as needed). The latter will accomplish what you're looking for without the use of any "spacer views".
// NSArray+PureLayout.h
// ...
/** Distributes the views in this array equally along the selected axis in their superview. Views will be the same size (variable) in the dimension along the axis and will have spacing (fixed) between them. */
- (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
alignedTo:(ALAttribute)alignment
withFixedSpacing:(CGFloat)spacing;
/** Distributes the views in this array equally along the selected axis in their superview. Views will be the same size (fixed) in the dimension along the axis and will have spacing (variable) between them. */
- (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
alignedTo:(ALAttribute)alignment
withFixedSize:(CGFloat)size;
// ...
see developer.apple' documentation, that is having nice description about the solution, https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html see the spacing and warping in that page, i think that is nice description, so no need to explain same thing over here
EDIT
Above link is now get disabled by apple, As from iOS 9, they have introduced Stackview, which is solution for all this.
Previously in above link the answer was same as answer provided by #rob
Related
I have a scroll view and one Plus button is there to add the extra text fields under the add button.
Please check the screenshot below for reference.
- (IBAction)Textfield:(id)sender
{
i++;//global declaration int
UITextField *textfield=[[UITextField alloc]init];
textfield.tag=i;
NSDictionary *viewsDictionary = {#"give-textfield-name here":self.textfield};
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-10-[give-textfield-name]-10-|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:viewsDictionary];
[self.textfield addConstraints:constraints];
}
Visual constraints example:
V:|-10-[give-textfield-name]-10-|
V means vertical constraints
| viewcontroller left margin &right margin
10 giving space from left margin .
And this is very basic thing of VisualConstraints ,you need to develop it more.
And one more thing you need to update the scrollviewcontent size after adding each textfield.
set the viewcontroller as freedom height and width.[update this also]
Hope it will help to start you program.
this is the link for visual constraints.
this is the link for autolayout.
Create textfields on clicking the plus button and add tags to textfields for fetching data from it.
let objForTextfield = UITextField()
objForTextfield.frame = CGRectMake(0, (scrollView.frame.origin.y + scrollView.frame.size.height ), 100, 100)
self.view.addSubview(objForTextfield)
scrollView.contentSize = CGSizeMake(scrollView.subviews.last?.frame.origin.y)!+(scrollView.subviews.last?.frame.size.height)!
firstly you need to group this view into three uiviews.
first one contain the two text field parts you want to show on buttonclick
2.Secondone contain upper part of that text fields
3.Thirdone contain lower part of that text fields
Then setheight constraintfor first view and vertical spacing bwn
First -Second and second - third.
At first time the height constraint must be zero for the first view
And priority of vertical spacing of second - third greater than priority of First -Second.
When the buttonclicks change theheight offirst view and also change the priority of that vertical spaces.
You can override viewDidLayoutSubviews and set content size of scrollview then call [self.view layoutIfNeeded]
Challenge time!
Imagine we have 2 content views:
UIView with dynamically height content (expandable UITextView) = RED
UIView as a footer = BLUE
This content is inside a UIScrollView = GEEN
How should I structure and handle the constraints with auto-layout to archive all the following cases?
I am thinking next basic structure to start with:
- UIScrollView (with always bounce vertically)
- UIView - Container
- UIView - DynamicHeightContent
- UIView - Sticky Footer
Keyboard handling should be done by code watching notifications UIKeyboardWillShowNotification and UIKeyboardWillHideNotification. We can chose to set the keyboard's end frame height to Container UIView bottom pin constraint or to the UIScrollView bottom contentInset.
Now, the tricky part is the sticky footer.
How we make sure the sticky footer UIView stays at the bottom if there is more screen available than the whole Container View?
How do we know the available screen space when the keyboard is shown/hidden? we'll surely need it.
Is is it right this structure I purpose?
Thank you.
When the text content of the UITextView is relatively short, the content view's subviews (i.e., the text view and footer) will not be able to dictate the size of their content view through constraints. That's because when the text content is short, the content view's size will need to be determined by the scroll view's size.
Update: The latter paragraph is untrue. You could install a fixed-height constraint either on the content view itself or somewhere in the content view's view hierarchy. The fixed-height constraint's constant could be set in code to reflect the height of the scroll view. The latter paragraph also reflects a fallacy in thinking. In a pure Auto Layout approach, the content view's subviews don't need to dictate the scroll view's contentSize; instead, it's the content view itself that ultimately must dictate the contentSize.
Regardless, I decided to go with Apple's so-called "mixed approach" for using Auto Layout with UIScrollView (see Apple's Technical Note: https://developer.apple.com/library/ios/technotes/tn2154/_index.html)
Some iOS technical writers, like Erica Sadun, prefer using the mixed approach in pretty much all situations ("iOS Auto Layout Demystified", 2nd Ed.).
In the mixed approach, the content view's frame and the scroll view's content size are explicitly set in code.
Here's the GitHub repo I created for this challenge: https://github.com/bilobatum/StickyFooterAutoLayoutChallenge. It's a working solution complete with animation of layout changes. It works on different sized devices. For simplicity, I disabled rotation to landscape.
For those who don't want to download and run the GitHub project, I have included some highlights below (for the complete implementation, you'll have to look at the GitHub project):
The content view is orange, the text view is gray, and the sticky footer is blue. The text is visible behind the status bar while scrolling. I don't actually like that, but it's fine for a demo.
The only view instantiated in storyboard is the scroll view, which is full-screen (i.e., underlaps status bar).
For testing purposes, I attached a double tap gesture recognizer to the blue footer for the purpose of dismissing the keyboard.
- (void)viewDidLoad
{
[super viewDidLoad];
self.scrollView.alwaysBounceVertical = YES;
[self.scrollView addSubview:self.contentView];
[self.contentView addSubview:self.textView];
[self.contentView addSubview:self.stickyFooterView];
[self configureConstraintsForContentViewSubviews];
// Apple's mixed (a.k.a. hybrid) approach to laying out a scroll view with Auto Layout: explicitly set content view's frame and scroll view's contentSize (see Apple's Technical Note TN2154: https://developer.apple.com/library/ios/technotes/tn2154/_index.html)
CGFloat textViewHeight = [self calculateHeightForTextViewWithString:self.textView.text];
CGFloat contentViewHeight = [self calculateHeightForContentViewWithTextViewHeight:textViewHeight];
// scroll view is fullscreen in storyboard; i.e., it's final on-screen geometries will be the same as the view controller's main view; unfortunately, the scroll view's final on-screen geometries are not available in viewDidLoad
CGSize scrollViewSize = self.view.bounds.size;
if (contentViewHeight < scrollViewSize.height) {
self.contentView.frame = CGRectMake(0, 0, scrollViewSize.width, scrollViewSize.height);
} else {
self.contentView.frame = CGRectMake(0, 0, scrollViewSize.width, contentViewHeight);
}
self.scrollView.contentSize = self.contentView.bounds.size;
}
- (void)configureConstraintsForContentViewSubviews
{
assert(_textView && _stickyFooterView); // for debugging
// note: there is no constraint between the subviews along the vertical axis; the amount of vertical space between the subviews is determined by the content view's height
NSString *format = #"H:|-(space)-[textView]-(space)-|";
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:#{#"space": #(SIDE_MARGIN)} views:#{#"textView": _textView}]];
format = #"H:|-(space)-[footer]-(space)-|";
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:#{#"space": #(SIDE_MARGIN)} views:#{#"footer": _stickyFooterView}]];
format = #"V:|-(space)-[textView]";
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:#{#"space": #(TOP_MARGIN)} views:#{#"textView": _textView}]];
format = #"V:[footer(height)]-(space)-|";
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:format options:0 metrics:#{#"space": #(BOTTOM_MARGIN), #"height": #(FOOTER_HEIGHT)} views:#{#"footer": _stickyFooterView}]];
// a UITextView does not have an intrinsic content size; will need to install an explicit height constraint based on the size of the text; when the text is modified, this height constraint's constant will need to be updated
CGFloat textViewHeight = [self calculateHeightForTextViewWithString:self.textView.text];
self.textViewHeightConstraint = [NSLayoutConstraint constraintWithItem:self.textView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1.0f constant:textViewHeight];
[self.textView addConstraint:self.textViewHeightConstraint];
}
- (void)keyboardUp:(NSNotification *)notification
{
// when the keyboard appears, extraneous vertical space between the subviews is eliminated–if necessary; i.e., vertical space between the subviews is reduced to the minimum if this space is not already at the minimum
NSDictionary *info = [notification userInfo];
CGRect keyboardRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
keyboardRect = [self.view convertRect:keyboardRect fromView:nil];
double duration = [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
CGFloat contentViewHeight = [self calculateHeightForContentViewWithTextViewHeight:self.textView.bounds.size.height];
CGSize scrollViewSize = self.scrollView.bounds.size;
[UIView animateWithDuration:duration animations:^{
self.contentView.frame = CGRectMake(0, 0, scrollViewSize.width, contentViewHeight);
self.scrollView.contentSize = self.contentView.bounds.size;
UIEdgeInsets insets = UIEdgeInsetsMake(0, 0, keyboardRect.size.height, 0);
self.scrollView.contentInset = insets;
self.scrollView.scrollIndicatorInsets = insets;
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
[self scrollToCaret];
}];
}
Although the Auto Layout component of this demo app took some time, I spent almost as much time on scrolling issues related to a UITextView being nested inside of a UIScrollView.
Instead of using a UIScrollView you would very likely be better off with a UITableView. It also might be better to not using auto-layout. At least, I've found it better to not use it for these sorts of manipulations.
Look into the following:
UITextView textViewDidChange
Change the size of the text view using sizeThatFits (limiting width and using FLT_MAX for height). Change the frame, not the contentSize.
Call UITableView beginUpdates/endUpdates to update the table view
Scroll to the cursor
UIKeyboardWillShowNotification notification
On NSNotification that comes through, you can call userInfo (a Dictionary), and the key UIKeyboardFrameBeginUserInfoKey. Reduce the frame of the table view based on the height of the size of the keyboard.
Scroll to cursor again (since the layouts will have all changed)
UIKeyboardWillHideNotification notification
The same as the show notification, just opposite (increasing the table view height)
To have the footer view stick to the bottom, you could add an intermediate cell to the table view, and have it change size depending on the size of the text and whether the keyboard is visible.
The above will definitely require some extra manipulation on your part - I don't fully understand all of your cases, but it should definitely get you started.
If I understand whole task, my solution is put "red" and "blue" views to one container view, and in the moment when you know size of dynamic content (red) you can calculate size of container and set scrollView content size.
Later, on keyboard events you can adjust white space between content and footer views
Xcode Interface Builder issue
Personally I do not like the way that interface builder works in Xcode. In this example I am trying to create a fairly complex view controller. On the viewDidLoad of the view controller I show a custom alert view (as such). It is not actually an alert view but more of a view that shows the user some information. I have a dimmed background view and a view on top of this. If I try to create this in interface builder it gets overly complicated as you cannot select the views in the background and move them etc without dropping subviews into the wrong views and so on...
Scenario
What I am trying to do is create a View which holds some labels and a button. The view controller has a difficulty property based on this it will have different text in the labels/amount of labels.
I.e. Easy -- 3 labels
Hard -- 4 labels
I create the dimmedView and alert(styled)View like this:
// Setup the dimmedView
UIView *dimmedView = [[UIView alloc] initWithFrame:self.view.frame];
dimmedView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.6];
// Setup the startingAlertView
UIView *startingAlertView = [[UIView alloc] init];
startingAlertView.backgroundColor = [UIColor whiteColor];
I then create the three/four labels based on some logic and add the necassary labels to the startingAlertView based on logic also.
The issue that is obvious is that at no point a frame for the view is set. This means that it is returning 0,0,0,0. What I would like to happen is the view to take the required height based on the labels added.
I am building for IOS7 and using Auto Layout. Should I be setting up constraints which would then adjust the relevant heights and locations in the view possibly?
I am building for IOS7 and using Auto Layout. Should I be setting up constraints which would then adjust the relevant heights and locations in the view possibly?
Yes. you don't use initWithFrame: under auto layout, or rather, you can, but the frame is ignored. Create your dimming view with a frame of CGRectZero, setting translatesAutoresizingMasksToConstraints to NO, add it to your main view and create constraints pinning it to all edges of the superview.
Then, add your alert view, again with a frame of zero and the translates... property set to NO. Create constraints to centre this view in your dimming view. This view will get its size from its subviews, since labels have an intrinsic size.
Add your labels as subviews of this view, with frame of zero and translates... set to NO. Depending on their content you may wish to set preferred max layout width or a width constraint.
Create constraints pinning your labels to the left and right edges of the superview, and lining your labels up in a vertical 'stack'. In each case you could add padding to give your alert a bit of a border.
This can look like a large amount of code, so you may want to read the articles I've written on visual format for auto layout and creating constraints in code, with the associated autolayout convenience category to make your life easier.
If you're going to the auto layout route, then you can add constraints that will keep the proper space between each label, and the proper space between the top and bottom of the view with the first and last labels. However, if you're not doing this in Interface Builder, you might as well skip using auto layout also, because it's fairly simple to just adjust the height of the view as you add labels.
You would start by setting the height of the view to the size of the top and bottom spaces that you want to have around the labels. Then each time you add a label, add to it the height of the label plus the height of the space you're putting between labels.
You could also wait until you've added all of the labels that you want, then set the height to the bottom label's y position plus its height plus the bottom space you want to have around the labels.
Yes, using autolayout you can get the bounds from the parent view.
Here is a quick example, notice that we are not using frame, and using CGRectZero for our UILabels, the positioning comes from updateConstraints instead. I am using Visual Format Language to layout the labels which I recommend if you are doing it programatically.
Here we are making the labels the width of the parent view and then just stacked on top of each other.
#import "View.h"
#implementation View{
UILabel *_label1;
UILabel *_label2;
UILabel *_label3;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_label1 = [[UILabel alloc] initWithFrame:CGRectZero];
_label1.translatesAutoresizingMaskIntoConstraints = NO;
_label1.text = #"LABEL 1";
_label2 = [[UILabel alloc] initWithFrame:CGRectZero];
_label2.translatesAutoresizingMaskIntoConstraints = NO;
_label2.text = #"LABEL 2";
_label3 = [[UILabel alloc] initWithFrame:CGRectZero];
_label3.translatesAutoresizingMaskIntoConstraints = NO;
_label3.text = #"LABEL 3";
[self addSubview:_label1];
[self addSubview:_label2];
[self addSubview:_label3];
}
[self updateConstraintsIfNeeded];
return self;
}
-(void)updateConstraints
{
[super updateConstraints];
NSDictionary *_viewsDictionary = NSDictionaryOfVariableBindings(_label1,_label2,_label3);
// Set the contraintsto span the entire width of the super view
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[_label1]-|"
options:0
metrics:nil
views:_viewsDictionary];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[_label2]-|"
options:0
metrics:nil
views:_viewsDictionary];
[self addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[_label3]-|"
options:0
metrics:nil
views:_viewsDictionary];
[self addConstraints:constraints];
// Last setup the vertical contraints other wise they will end up in a random place
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[_label1]-[_label2]-[_label3]"
options:0
metrics:nil
views:_viewsDictionary];
[self addConstraints:constraints];
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
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'd like to embed views into each other (in the current case into a scrollview to allow displaying taller content than the screen's size) and use iOS 6.0's AutoLayout feature in order to avoid constant calculation of content size's.
I have the following ViewController's view, containing a UIScrollView:
I'd like to display my ChildVC's view in this scrollview:
Please notice that the label is multiline and contains a 'lot of' text, also auto-layout constraints are defined (escpecially with a greater-than-or-equal to the height property).
I create the childVC and add it to the main VC's view via the following code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
childVC = [[ChildVC alloc] initWithNibName:#"ChildVC" bundle:nil];
self.myScrollView.translatesAutoresizingMaskIntoConstraints = NO;
childVC.view.translatesAutoresizingMaskIntoConstraints = NO;
[self.myScrollView addSubview:childVC.view];
NSDictionary *viewsDictionary = #{ #"subView" : childVC.view};
NSString* constr = [NSString stringWithFormat:#"|-0-[subView(%f)]-0-|", self.myScrollView.frame.size.width];
[self.myScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:constr options:0 metrics: 0 views:viewsDictionary]];
[self.myScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[subView]-(>=0)-|" options:0 metrics: 0 views:viewsDictionary]];
}
The constraints for the content-view (which I guess will be the childVC's view) are the only stuff set up in code, because I only want a vertical scrollbar, I want the child-view's content to (horizontally) shrink to be displayed in the scrollviewer.
And my output is this:
The UI is displayed, shrinked but my label does not resize vertically therefore it does not display the whole text :(
I tried to set up translatesAutoresizingMaskIntoConstraints to the label here and there without any success.
I'd appreciate any help because I'm struggling with this for days now :/
Personally, I think those constraints and the auto layout feature just don't always work exactly as we want them to. (Especially on different screen sizes).
So I normally add a few line of code in the viewDidLoad method to move certain objects around by a few pixels. You can also change sizes and so on.
Literally a simple movement like this does the trick without the user noticing any animations:
object_name.frame = CGRectOffset(object_name.frame, 0, -40.0f);