I have 10 UIButtons one below each other. I want to change its height according to the iPhone screen size. It should look bigger in iPhone 6 plus screen and smaller in iPhone 5s screen. How to do it using autolayout.
You first pick a UIView and set its constraints like top, bottom, leading and trailing, after that drag all UIButtons on the view and set all buttons constraints like top, bottom, leading, trailing and equal width and equal height constraints you can check these images
iPhone 7 Plus screen:-
and iPhone 5s screen
Xcode View
To do this, you must add the height of each button base on the percentage (%) of device screen size. so that button size can vary according to device(iPhone4s, 5s, 6 plus) screen size.
Now I’m going to add constraints programmatically using KVConstraintExtensionsMaster library. Try below code by calling below method from viewDidLoad of your ViewController.
- (void)configureScrollViewHierarchyAndApplyConstraint
{
CGFloat mainScreenHeight = [[UIScreen mainScreen] bounds].size.height;
// here baseScreenHeight is default screen height size for which we are implementing.
CGFloat baseScreenHeight = 667; // here default iPhone 6 height
// Note: try by changing baseScreenHeight via any iPhone screen height(480, 568, 667, 736) and see the changes in button height & space
// here fixed space and height are fixed size with respect to iPhone 6 height.
CGFloat fixedSpace = 28;
CGFloat fixedHeight = 150;
// ratio is responsible to increase or decrease button height depends on iPhone device size.
CGFloat ratio = mainScreenHeight/baseScreenHeight;
CGFloat baseSpace = fixedSpace * ratio;
CGFloat baseHeight = fixedHeight * ratio;
// prepare scrollView for autolayout
UIScrollView *scrollView = [UIScrollView prepareNewViewForAutoLayout];
scrollView.backgroundColor = [UIColor brownColor];
[self.view addSubview:scrollView];
// prepare containerView for autolayout
UIView *containerView = [UIView prepareNewViewForAutoLayout];
containerView.backgroundColor = [UIColor colorWithWhite:1 alpha:0.95];
[scrollView addSubview:containerView];
// To add Leading and Trailing constraint
[scrollView applyLeadingAndTrailingPinConstraintToSuperviewWithPadding:baseSpace];
// To add Top and Bottom constraint
[scrollView applyTopAndBottomPinConstraintToSuperviewWithPadding:baseSpace];
// To add Top and Bottom constraint of containerView
[containerView applyTopAndBottomPinConstraintToSuperviewWithPadding:0];
// To Define the containerView X Position by adding HorizontalCenter constraint
[containerView applyConstraintForHorizontallyCenterInSuperview];
// Here To Define the width
[containerView applyEqualWidthPinConstrainToSuperview]; // Or
//[containerView applyLeadingPinConstraintToSuperviewWithPadding:10];
NSInteger count = 20;
UIButton *previousContentButton = nil;
for (NSInteger i = 0; i < count; i++)
{
UIButton *contentButton = [UIButton prepareNewViewForAutoLayout];
if (i&1) {
[contentButton setBackgroundColor:[UIColor greenColor]];
}else{
[contentButton setBackgroundColor:[UIColor redColor]];
}
[contentButton setTag:i];
[containerView addSubview:contentButton];
// Define the contentButton Size
[contentButton applyLeadingAndTrailingPinConstraintToSuperviewWithPadding:baseSpace];
[contentButton applyHeightConstraint:baseHeight];
if (i == 0) // for first
{
// To add top constraint
[contentButton applyTopPinConstraintToSuperviewWithPadding:baseSpace];
}
else if (i == count-1) // for last
{
// To add vertical constraint between two buttons
[previousContentButton applyConstraintFromSiblingViewAttribute:NSLayoutAttributeBottom toAttribute:NSLayoutAttributeTop ofView:contentButton spacing:baseSpace];
// To add bottom constraint
[contentButton applyBottomPinConstraintToSuperviewWithPadding:baseSpace];
}
else
{
// To add vertical constraint between two buttons
[previousContentButton applyConstraintFromSiblingViewAttribute:NSLayoutAttributeBottom toAttribute:NSLayoutAttributeTop ofView:contentButton spacing:baseSpace];
}
previousContentButton = contentButton;
}
[containerView updateModifyConstraints];
}
a simple example:
drag a button from the object library to your viewcontroller's view in storyboard
ctrl drag from your button to your view (drag to the left or the right) and choose center vertically in container
ctrl drag from your button to your view (drag to the top or the bottom) and choose center horizontally in container
ctrl drag FROM the button TO the button (yes, the same button) and choose aspect ratio
in the size inspector check the button's aspect ratio constraint to have a multiplier of 1:1
ctrl drag from your button to your view (drag to the left or the right) and choose equal widths
7 .in the size inspector check the button's equal width constraint to have a multiplier of 1:3 (or whatever value you like - 1:3 means that that the button's width is one third of the view's width)
You can check this answer.
Just use Verticle UIStackView.
Related
After 2 days behind the implementation of a UIScrollView, I give up to do it by myself, despite It should be simple but I am not able to make it.
I need a vertical scrollview with Autolayout (I have 2 backgrounds images that need to scale), I have Xcode 7.2, iOs deployment target 9.0 and I am using the storyboard
The scrollview is a "new account" form with a lot of content, So it can have a fixed height
I tried lots of options but no one of them work, at the momment I have a scrollview with a "content view" with all of the components.
Problems:
1.- How can I put more content in the scrollview? If I try to put more compoments(more than the height of ViewControler), it automatically moves my components to be inside the ViewController rectangle
2.- How I can make it scrollable? If I pin the content view to the top, bottom, left, right and make it with fixed height (like 1000) it doesn't scroll
Any help would be appreciated
Scrollview will scroll until the end of last UI object. But this is not the default behaviour of the scrollview. You should explicitly set the contentSize on your scrollview. Check below example.
-(void)addImage {
int count = 20;
UIScrollView * scView = [[UIScrollView alloc] init];
scView.frame = CGRectMake(30, 60, self.view.frame.size.width, 60);
UIButton * btn;
UIImageView * imgV;
float imgWidth = 44;
for(int i=0;i<count;i++) {
imgV = [[UIImageView alloc] init];
imgV.frame = CGRectMake(i*imgWidth, 0, imgWidth, 44);
imgV.image = [UIImage imageNamed:#"gitar"];
[scView addSubview:imgV];
}
[scView setContentSize:CGSizeMake(imgWidth*count, 50)];
[scView setBackgroundColor:[UIColor redColor]];
[self.view addSubview:scView];
}
What the above method does is, it will add 20 images in scrollview and at the end after adding all the images to scrollview in for loop I set contentSize to Scrollview. This step makes my scrollview to scroll until the content available. My example showing for Horizontal scroll, you have to change this behaviour to vertical scroll.
I want to top align the text in tableView cell.I created a cell subclass and added the following in layout subview.However it doesn't work.What am I missing?
- (void)layoutSubviews {
[super layoutSubviews];
CGRect newFrame = self.textLabel.frame;
newFrame.origin.y = CGRectGetMinY (self.contentView.bounds);
[self.textLabel setFrame:newFrame];
}
Basically what you are doing is putting the UILabel at high place in the cell, but the labels height might still be big, and as a result the text is not at the top.
Vertical align in UILabel is a problem. By default a text in UILabel is center vertical aligned. If you want to see the text at the top you will have to change the label height to fit the size:
[self.textLabel sizeToFit];
// Will keep the same X and Y so label will look like it moved
// to the upper left corner of it's frame.
I'm writing an iOS 7 app and I have a rectangle with a label on it. The label is aligned center and is created to be the same size as the view (for simplicity when increasing the size).
The desired effect is when clicked, to animate the view to full size, with the label staying centered the whole time.
I currently have tried:
Setting the top, left, bottom, and right constraints on the label to 0
Setting the height and width of the label to the view at the initial size and animating it to the full size along with the view animation
Setting the label's top and left constraints to 0 and animating the size to the full size
None of these produce the desired output. Each time the label seems to just snap to its final size before the view even starts animating.
Here is my code:
_viewHeightConstraint.constant = self.view.frame.size.height;
_viewWidthConstraint.constant = self.view.frame.size.width;
_viewTopConstraint.constant = 0;
_viewLeftConstraint.constant = 0;
_labelWidthConstraint.constant = self.view.frame.size.width;
_labelHeightConstraint.constant = self.view.frame.size.height;
[self.view needsUpdateConstraints];
[UIView animateWithDuration:1.5f
animations:^(void) {
[self.myView layoutIfNeeded]; //perform relayout of view containing label before relayout of entire view
[self.view layoutIfNeeded];
}];
I'm not sure if I've provided everything necessary, as I'm still new to autolayout. However, the desired effect is a view that animates to full size while the label in the center stays centered.
Add these constraints to your label:
(UIViewAutoresizingFlexibleLeftMargin |
UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin |
UIViewAutoresizingFlexibleBottomMargin)
Why do you call needsUpdateConstraints?
All the changes occur there. Just call:
...
_labelWidthConstraint.constant = self.view.frame.size.width;
_labelHeightConstraint.constant = self.view.frame.size.height;
[UIView animateWithDuration:1.5f
animations:^(void) {
[self.view layoutIfNeeded];
}];
On a side note, why do you set height and width constraints on the label? You can just add center horizontal and vertical constraints. I don't think you can set vertical text alignment for a label. But that was not your initial issue.
Consider a UIScrollView with a single subview. The subview is an UIImageView with the following size constraints:
Its height must be equal to the height of the UIScrollView.
Its width must be the width of the image scaled proportionally to the height of the UIImageView.
It is expected that the width of the UIImageView will be bigger than the width of the UIScrollView, hence the need for scrolling.
The image might be set during viewDidLoad (if cached) or asynchronously.
How do you implement the above using autolayout, making as much use as possible of Interface builder?
What I've done so far
Based on this answer I configured my nib like this:
The UIScrollView is pinned to the edges of its superview.
The UIImageView is pinned to the edges of the UIScrollView.
The UIImageView has a placeholder intrinsic size (to avoid the Scrollable Content Size Ambiguity error)
As expected, the result is that the UIImageView is sized to the size of the UIImage, and the UIScrollView scrolls horizontally and vertically (as the image is bigger than the UIScrollView).
Then I tried various things which didn't work:
After loading the image manually set the frame of UIImageView.
Add a constraint for the width of the UIImageView and modify its value after the image has been loaded. This makes the image even bigger (?!).
Set zoomScale after the image is loaded. Has no visible effect.
Without autolayout
The following code does exactly as I want, albeit without autolayout or interface builder.
- (void)viewDidLoad
{
{
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:scrollView];
self.scrollView = scrollView;
}
{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height)];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.scrollView addSubview:imageView];
self.scrollView.contentSize = imageView.frame.size;
self.imageView = imageView;
}
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
[self layoutStripImageView];
}
- (void)layoutStripImageView
{ // Also called when the image finishes loading
UIImage *image = self.imageView.image;
if (! image) return;
const CGSize imageSize = image.size;
const CGFloat vh = self.scrollView.frame.size.height;
const CGFloat scale = vh / imageSize.height;
const CGFloat vw = imageSize.width * scale;
CGSize imageViewSize = CGSizeMake(vw, vh);
self.imageView.frame = CGRectMake(0, 0, imageViewSize.width, imageViewSize.height);
self.scrollView.contentSize = imageViewSize;
}
I'm trying really hard to move to autolayout but it's not being easy.
Under the autolayout regime, ideally the UIScrollView contentSize is solely determined by the constraints and not set explicitly in code.
So in your case:
Create constraints to pin the subview to the UIScrollView. The constraints have to ensure the margin between the subview and the scroll view are 0. I see that you have already tried this.
Create a height and a width constraint for your subview. Otherwise, the intrinsic size of the UIImageView determines its height and width. At design time, this size is only a placeholder to keep Interface Builder happy. At run time, it will be set to the actual image size, but this is not what you want.
During viewDidLayoutSubviews, update the constraints to be actual content size. You can either do this directly by changing the constant property of the height and width constraint, or calling setNeedsUpdateConstraints and overriding updateConstraints to do the same.
This ensures that the system can derive contentSize solely from constraints.
I've done the above and it works reliably on iOS 6 and 7 with a UIScrollView and a custom subview, so it should work for UIImageView too. In particular if you don't pin the subview to the scroll view, zooming will be jittery in iOS 6.
You may also try creating height and width constraints that directly reference a multiple of the height and width of the scroll view, but I haven't tried this other approach.
translatesAutoresizingMaskIntoConstraints is only required when you instantiate the view in the code. If you instantiate it in the IB it's disabled by default
In my opinion the UIImageView should fill the ScrollView. Later I'd try setting the zoom of the scrollview to the value that suits you well so the image can only be panned in one direction
In my case it was a full width UIImageView the had a defined height constraint that causing the problem.
I set another constraint on the UIImageView for the width that matched the width of the UIScrollView as it is in interface builder then added an outlet to the UIViewController:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *imageViewWidthConstraint;
then on viewDidLayoutSubviews I updated the constraint:
- (void) viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.imageViewWidthConstraint.constant = CGRectGetWidth(self.scrollView.frame);
}
This seemed to do the trick.
I'd like to have some views fixed on the top of the screen,
some other on the bottom and a single fixed size view in the equal distance between the top and bottom views.
I cannot figure out how to do this with Autolayout constraints. Do I need to add some spacer views to the UI, or calculate the desired position programatically?
You can do this with only one additional view. It'd look like this:
stuff_on_top
middle_view (with fixed size view inside)
stuff_on_bottom
There'd be vertical spacing constraints between stuff_on_top & middle_view and between middle_view & stuff_on_bottom. fixed size view would be centered horizontally and vertically in middle_view.
The other way of doing this would be two put two spacer views: between stuff_on_top & middle_view and between middle_view & stuff_on_bottom. Then you'd add a constraint that heights of spacing views are equal.
Check out this category: https://github.com/jrturton/UIView-Autolayout
Then you can do something as simple as this...
#import "UIView+AutoLayout.h"
...
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *topView = [UIView autoLayoutView];
UIView *centralContainerView = [UIView autoLayoutView];
UIView *centralView = [UIView autoLayoutView];
UIView *bottomView = [UIView autoLayoutView];
topView.backgroundColor = [UIColor redColor];
bottomView.backgroundColor = [UIColor redColor];
centralView.backgroundColor = [UIColor greenColor];
[self.view addSubview:topView];
[self.view addSubview:centralContainerView];
[centralContainerView addSubview:centralView];
[self.view addSubview:bottomView];
//Pins the topView to the top, left and right edges of its superview (in iOS 7, it uses the topLayoutGuide)
[topView pinToSuperviewEdges:JRTViewPinTopEdge|JRTViewPinLeftEdge|JRTViewPinRightEdge inset:0 usingLayoutGuidesFrom:self];
//Constrains the height of topView to 75pts (if a value is passed as zero, no constrain is applied to that axis)
[topView constrainToSize:CGSizeMake(0, 75)];
//Pins the centralContainerView to the left and right edges of its superview
[centralContainerView pinToSuperviewEdges:JRTViewPinLeftEdge|JRTViewPinRightEdge inset:0];
//Pins the top of centralContainerView to the bottom of topView
[centralContainerView pinEdge:NSLayoutAttributeTop toEdge:NSLayoutAttributeBottom ofItem:topView];
//Pins the bottom of centralContainerView to the top of bottomView
[centralContainerView pinEdge:NSLayoutAttributeBottom toEdge:NSLayoutAttributeTop ofItem:bottomView];
//Centers centralView on the Y axis of its superview
[centralView centerInContainerOnAxis:NSLayoutAttributeCenterY];
//Pins the centralView to the left and right edges of its superview
[centralView pinToSuperviewEdges:JRTViewPinLeftEdge|JRTViewPinRightEdge inset:0];
//Constrains the height of topView to 100pts
[centralView constrainToSize:CGSizeMake(0, 100)];
//Pins the topView to the bottom, left and right edges of its superview (in iOS 7, it uses the bottomLayoutGuide)
[bottomView pinToSuperviewEdges:JRTViewPinBottomEdge|JRTViewPinLeftEdge|JRTViewPinRightEdge inset:0 usingLayoutGuidesFrom:self];
//Constrains the height of topView to 75pts
[bottomView constrainToSize:CGSizeMake(0, 75)];
}
And you get an output like this:
Edit:
I didn't see the interface-builder tag and just jumped to conclusions... An interface builder alternative would work similar to above.. You would need to have three main views, one pinned to the top and the other pinned to the bottom.. Then one with a flexible width that is pinned to the other two views.
You can then centre a fourth view with a fixed height in your middle view. This will then give you the result you are looking for
I code this
https://github.com/damienromito/UIView-AutoYPositioning
But I think a solution with AutoLayout exist...