I am using xcode 5 with storyboards and autolayout turned ON. My layouts of one of my ViewControllers has a UIButton with an image background. The view controller also has 3 buttons anchored to the bottom of the view and some labels above. I laid out my storyboards to a 4" retina display (ie: iphone 5+) and the issue I have is when simulating to a 3.5" display (ie: iphone 4, 4s, etc)
I have pinned the height of the anchored buttons at the bottom to remain constant. What I want is when the app runs on a 3.5" display iphone, that the UIButton with the image will resize smaller (keeping all other labels & buttons same size & spacing). So the aspect ratio would remain the same for that UIButton, it would just get smaller.
I haven't been able to find anything in the auto layout tutorial or others online about how to set up constraints to do this (where the height/width remain proportional).
I think what you really want is as follow:
1.Label's top is 100 from its superView and its bottom is 68 from its superView
2.In 4" display, its size is 200x400 with ratio .5
3.In 3.5" display, its size is 312x624 with ratio .5
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// create search bar
CGRect frame = CGRectMake(60, 100, 200, 400);
_label = [[UILabel alloc] initWithFrame:frame];
_label.backgroundColor = [UIColor blueColor];
[self.view addSubview:_label];
// layout search bar
_label.translatesAutoresizingMaskIntoConstraints = NO;
// height
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:_label
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationLessThanOrEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:0
constant:400];
// width
[_label addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:_label
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:_label
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[_label addConstraint:constraint];
// vertical
NSDictionary *views = NSDictionaryOfVariableBindings(_label, self.view);
NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-100-[_label]-68-|"
options:0
metrics:nil
views:views];
[self.view addConstraints:constraints];
// horizontal
constraint = [NSLayoutConstraint constraintWithItem:_label
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0];
[self.view addConstraint:constraint];
}
Checkout the 'Taking Control of Auto Layout in Xcode 5' video from the WWDC 2013 Videos -- Quickly: the top part of the Add New Constraints area is probably what your looking for
I guess preserving aspect ratio is NOT possible in auto layouts via interface builder. #fail.
According to this: http://www.raywenderlich.com/50319/beginning-auto-layout-tutorial-in-ios-7-part-2, at the very end of the tutorial.
So, i guess I need to do this programmatically somehow.
Related
I have searched all day for a solution to this problem. I am trying to have my UILabels autosize themselves based on the length of the text inside a tableViewHeaderView. Normally, with my UILabels inside a UIView, I would set top, leading, and trailing constraints to the UILabel and it would work just like how I wanted. However, I can't get that working inside a tableViewHeaderView. I am able to set top and leading constraints but my trailing constraint doesnt seem to be working. The text goes beyond the width of the screen.
Setting the preferredMaxLayoutWidth property to a number solves the issue but I don't want to have to hard code that.
Correct me if I am wrong, but setting the leading and trailing constraints should be able to give me the width of the view does it not? Then I could set preferredMaxLayoutWidth with that value. But that value is 2403 which is way longer than the width of the screen.
Anyone else experience this?
#import "CustomTableViewHeader.h"
#implementation ReplyHeader{
UILabel *questionLabel;
}
questionLabel = [[UILabel alloc]init];
questionLabel.text = #"SAMPLE TEXT: I had a question about how I can be a better human being using your method. I belive it is an integral part of what it means to be a human so I want to learn more if you are able give more details about it. I also found that what you said about the dogs out there is very cool and would love to learn more about that.";
questionLabel.font = [UIFont fontWithName:#"AvenirNext-Regular" size:17];
questionLabel.lineBreakMode = NSLineBreakByWordWrapping;
questionLabel.numberOfLines = 0;
questionLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:questionLabel];
Constraints
[self addConstraint:[NSLayoutConstraint constraintWithItem:questionLabel attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0f constant:5.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:questionLabel attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTrailing multiplier:1.0f constant:-5.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:questionLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0f constant:10]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:questionLabel attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0f constant:-10]];
update view
- (void)viewDidLayoutSubviews
{
questionLabel.preferredMaxLayoutWidth = questionLabel.frame.size.width;
[self layoutIfNeeded];
}
UITableView's do not get along with AutoLayout. Both header and footer have inherit that problem.
As you might already know, UITableViews are fancy UIScrollViews. If you have ever tried to work with a scroll view, lots of elements and AutoLayout you must know that it does not work well. It lags. That's why UITableView reuse cells. It improves performance and they do not use AutoLayout.
You should try something like
// with this you will get the most compressed size for your view if the constraints properly define it's size
CGFloat height = [footer systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
footer = CGRectMake(CGRectGetMinX(view.frame), CGRectGetMinY(footer.frame), CGRectGetWidth(footer.bounds), height);
// This will tell the layout engine to ignore the view and not try to resize it
view.autoresizingMask = UIViewAutoresizingNone;
self.tableView.tableFooterView = footer;
What i want to achieve is set buttons/images Horizontally Centered with main view, including its width set to 75% of the screen width.
I want to fit like 7 such images/buttons vertically on the screen (line-by-line).
I am using the following code, which is working perfect:
UIImageView *l1 = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20, 100, 100)];
[l1 setImage:[UIImage imageNamed:#"level-1"]];
[l1 setContentMode:UIViewContentModeScaleAspectFit];
l1.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:l1];
NSLayoutConstraint *c1 = [NSLayoutConstraint
constraintWithItem:l1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTop
multiplier:1.0f
constant:20.f
];
[self.view addConstraint:c1];
NSLayoutConstraint *c1b = [NSLayoutConstraint
constraintWithItem:l1
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0f
constant:0.f
];
[self.view addConstraint:c1b];
NSLayoutConstraint *c1c = [NSLayoutConstraint
constraintWithItem:l1
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:0.75f
constant:0.f
];
[self.view addConstraint:c1c];
Instead of repeating the same code for different images, i want to use some iteration process and increment the image name [UIImage imageNamed:#"level-xxx"]] and bind top position to the bottom position of the last added item.
How it would be possible? Thx
iOS 9 introduces a new class called UIStackView that allows you to do this :
https://www.hackingwithswift.com/read/31/2/uistackview-by-example
There are even back-ports of this to iOS 7 on Github.
Some people might downvote me for this but sometimes , when it comes to autolayout I just say , $##& this I'll write the layout code myself. And this is one of those cases (especially when UIStackView is not available).
First create all your views and store them in an array in viewDidLoad.
Override viewDidLayoutSubviews on your view controller and loop through the array , set the frames and lay them out one by one.
-(void)viewDidLayoutSubviews
{
NSArray* imageList = self.imageViewList
// calculate it if necessary
CGPoint startPoint = CGPointMake(200,200)
CGFloat spacing = 50
for(UIView *view in imageList) {
view.center = startPoint
startPoint = CGPointMake(startPoint.x , startPoint.y + spacing)
}
}
This is way more obvious , way easier to debug than a mess of constraints created in code.
I created a UISlider programmatically, and after adding two auto layout constraints the slider bar no longer appears. I am seeing the nub / circle for the slider, but nothing else.
// create slider programmatically
_sliderFrame = CGRectMake(10.0f, 10.0f, 250.0f, 400.0f);
_sliderCalibrate = [[UISlider alloc] initWithFrame:_sliderFrame];
_sliderCalibrate.minimumValue = 1.0f;
_sliderCalibrate.maximumValue = 100.0f;
_sliderCalibrate.value = 50.0f;
// manually specify Auto Layout constraints in code
[_sliderCalibrate setTranslatesAutoresizingMaskIntoConstraints:NO];
// add slider to view
[self.view addSubview:_sliderCalibrate];
NSLayoutConstraint *centerSliderX = [NSLayoutConstraint constraintWithItem:_sliderCalibrate
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual toItem:_sliderCalibrate.superview
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
NSLayoutConstraint *centerSliderY = [NSLayoutConstraint constraintWithItem:_sliderCalibrate
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual toItem:_sliderCalibrate.superview
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0];
[_sliderCalibrate.superview addConstraints:#[centerSliderX, centerSliderY]];
You shouldn't set any frame when using auto layout, so just create the slider with [UISlider new]; Add either a width constraint, or delete the centerX constraint, and add constraints to both edges of the superview to give the slider a non-zero width.
By calling setFrame: for initializing an object, you don't need to call setTranslatesAutoresizingMaskIntoConstraints: with the argument NO on views that you place manually.
Just remove this line of code.
I'm working on this iPad app and I'm thinking of using auto layout to make my life easier. I created this side bar control that allows users to change to a different page (just like the tab bar controller, but this is along the left side of the iPad). Now in landscape orientation I want the width of this view to be 256 px but when the iPad is in Portrait orientation I want this view to have a width of 100 px. How can I use auto layout to pin the width of the view depending on the interface orientation?
You can use the updateViewConstraints call to modify the layout on orientation change.
The example below creates a view programatically but you could wire up the width constraint of you side bar in interface builder to achieve the same thing.
For example:
//create a custom view using autolayout, this is the equivalent of you side bar
- (void)viewDidLoad{
[super viewDidLoad];
//create a custom view
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
UIView *vw=[[UIView alloc] init];
self.customView =vw;
self.customView.backgroundColor = [UIColor blackColor];
[self.customView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:self.customView];
NSArray *arr;
//horizontal constraints
arr = [NSLayoutConstraint constraintsWithVisualFormat:#"|-20-[vw]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(vw)];
[self.view addConstraints:arr];
//vertical constraints
arr = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-20-[vw(200)]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(vw)];
[self.view addConstraints:arr];
}
- (void)updateViewConstraints{
[super updateViewConstraints];
//remove the existing contraint
if(self.widthConstraint!=nil){
[self.view removeConstraint:self.widthConstraint];
}
//portait set width to 100
if(UIInterfaceOrientationIsPortrait(self.interfaceOrientation)){
self.widthConstraint = [NSLayoutConstraint constraintWithItem:self.customView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:0
multiplier:1.0
constant:100.0];
}
//landscape set width to 256
else{
self.widthConstraint = [NSLayoutConstraint constraintWithItem:self.customView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:0
multiplier:1.0
constant:256.0];
}
[self.view addConstraint:self.widthConstraint];
}
I'm trying to build a rather simple animated custom UI using the Auto Layout API newly available iOS 6. The custom view I'm building has a circle that I want to be both vertically and horizontally centered.
Unfortunately I can't figure out why my constraints appear to work fine for UIButton, and UILabel elements but yield weird results when I use a custom view with and custom CALayer (in this case a circle, that will eventually be animated).
To be clear I don't want my view to expand to fill the whole screen, but rather to have dynamic "padding" so that the view is vertically centered both on the iPhone 4 and 5. I should also note that I'm very new to Cocoa and UIKit.
RootViewController.m:
...
- (void)viewDidLoad {
[super viewDidLoad];
// Create Circle View
CGRect circle_view_rect = CGRectMake(0, 0, 100, 100);
UIView *circle_view = [[UIView alloc] initWithFrame:circle_view_rect];
// Create Circle Layer
CircleLayer *circle_layer = [[CircleLayer alloc] init];
circle_layer.needsDisplayOnBoundsChange = YES;
circle_layer.frame = circle_view.bounds;
[circle_view.layer addSublayer:circle_layer];
// Enable Auto Layout
[circle_view setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:circle_view];
// Center Vertically
NSLayoutConstraint *centerYConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0
constant:0.0];
[self.view addConstraint:centerYConstraint];
// Center Horizontally
NSLayoutConstraint *centerXConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0.0];
[self.view addConstraint:centerXConstraint];
}
...
CircleLayer.m:
...
- (void)drawInContext:(CGContextRef)context {
CGContextAddArc(context, 50, 50, 50, 0.0, 2*M_PI, 0);
CGContextSetFillColorWithColor(context, [UIColor yellowColor].CGColor);
CGContextFillPath(context);
}
...
Basically the constraints I've implemented are:
center vertically inside parent view
center horizontally inside parent view
And this is the result I get:
Any help would be greatly appreciated, I've been pondering this one for a few days now.
Thanks
Try adding a height and width constraint to your circle_view. I couldn't even get just a pain square view to appear at all without adding those (using your code, minus the layer stuff).
NSLayoutConstraint *heightConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:100.0];
[circle_view addConstraint:heightConstraint];
NSLayoutConstraint *widthConstraint =
[NSLayoutConstraint constraintWithItem:circle_view
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:100.0];
[circle_view addConstraint:widthConstraint];
Just to add to rdelmar's answer:
The core issue is that as soon as you go the NSLayoutConstraint route, and specify setTranslatesAutoresizingMaskIntoConstraints:NO, the frame you made with CGRectMake is rendered irrelevant for AutoLayout purposes. That's why it didn't use the info from the frame's height and width.