autolayout update image size when rotating - ios

I'm very new with auto layout. I have a simple UIViewController that contains a View and a UIImageView.
On portrait mode this image is 320x115px but I would like to reduce the image height when rotating to landscape.
Currently I have the following constraints on my image.
-Height Equals 115px
-Trailing Space to superview
-Leading Space to superview
-Top Space to supverview
How to make my UIView have proportional vertical size when rotating from portrait to landscape?
Thanks for your help,
Sébastien.

If you want the same proportional size as 115 is to 480 (0.24), then you use the multiplier parameter in constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:.
In the example below, I made an IBOutlet to the height constraint in IB (heightCon), then remove that in code and add another one that is based on a fraction of self.view's height (iv is my outlet to the image view):
- (void)viewDidLoad {
[super viewDidLoad];
[self.iv removeConstraint:self.heightCon];
NSLayoutConstraint *newHeightCon = [NSLayoutConstraint constraintWithItem:self.iv attribute:NSLayoutAttributeHeight relatedBy:0 toItem:self.view attribute:NSLayoutAttributeHeight multiplier:.24 constant:0];
[self.view addConstraint:newHeightCon];
}

Related

AutoLayout two labels with 30:70 ratio proportional

I have two labels green and yellow and i am trying to make them always be in the same proportional to the screen width. I tried aspect ratio and changing the priority etc but not get the final result. I will be very thankful if someone can help me
You need to set up constraints for the widths of the views related to the width of the superview.
In your case:
Pin the left side of the green view to the view.
Pin the right side of the yellow view to the view.
Make the horizontal spacing between the green and yellow views to be zero.
Now create a constraint and apply it:
- (void)viewDidLoad {
[super viewDidLoad];
[self setupWidthConstraints];
}
- (void)setupWidthConstraints {
NSLayoutConstraint *widthConstraint;
widthConstraint = [NSLayoutConstraint constraintWithItem:self.greenView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:0.30
constant:0.0];
[self.view addConstraint:widthConstraint];
}
This constrains the green view's width to 30% of the view width. And since the yellow view is pinned to the side and the green view, it will take up the remaining 70%.
If you want to see this in action you can get an example project that demonstrates exactly this from https://bitbucket.org/abizern/so27659421/get/master.zip

AutoLayout doesn't resize properly

This is my view's hierarchy
superview
-wrapperview
-- childview
--childview
When I using autolayout, what I want is all subviews will have width equal with superview. But just wrapperview is equal
Example:
- In portrait: superview = wrapperview = 768 but childviews is 1024
- In landscape: superview = wrapperview = 1024 but childview is 768
Anyone know what's happened there?
If the child view's is pinned to the edges of your super view and it's content is bigger than the superview (which is likely in your case), then it will push the view out if you're using the visual formatting language.
For example, suppose your childView is a UIImageView, and has a image with dimension 1024 x 1024.
If your auto layout is something like:
[self.wrapperView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[imageView]|" options:0 metrics:nil views:views]];
Then your imageView will push the wrapperView out.
What you need to do is use this method of constraints, which is a bit more explicit:
[self.wrapperView addConstraint:[NSLayoutConstraint constraintWithItem:self.childView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.wrapperView attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]];
This tells autolayout, your child view's width should be equal to wrapper view's width, and never greater than (as specified by the NSRelationEqual flag).

How to configure a UIScrollView content size to have a fixed width and a dynamic height?

I have a UIScrollView contained in another container view with constraints set to take all the space of the container view. That is to say that I don't have a fixed width or height on the scroll view.
In my scroll view I programmatically add subviews. Each subview (content view) is loaded from a xib.
In the xib I set an arbitrary size to the root view (500x500) but I would like that view width to be automatically resized to the scroll view width (the scroll view width being the container width).
I don't want the user to be able to scroll horizontally.
I tried different solutions always resulting in the scroll view being scrollable horizontally.
I tried to tweak the content view hugging and compression property to different values on the horizontal axis with no success.
I don't want to set fixed widths on my views because I want them to take the width of the container view.
Thanks in advance if you have any suggestions.
Late answer with what I came up with at that time.
As I'm using Autolayout, VChemezov answer is not really satisfying.
My content views have a top, bottom, leading, width set of constraints. (width instead of trailing which is what I was doing in the first place but it was not working).
So now I have something like this:
NSLayoutConstraint *leading = [NSLayoutConstraint
constraintWithItem:messageView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.conversationScrollView
attribute:NSLayoutAttributeLeading
multiplier:1.0f
constant:0.0f];
NSLayoutConstraint *width = [NSLayoutConstraint
constraintWithItem:messageView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.conversationScrollView
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:0.0f];
[self.conversationScrollView addConstraints:#[ top, leading, width ]];
With the width of the content view equal to the width of the scroll view.
Autolayout will not adjust view size inside UIScrolView. You should layout and resize this views by yourself. There is two basic solutions:
Explicitly set width of subview before adding it to UIScrollView
Subclass UIScrollView and override setFrame and layoutSubviews like this
#implementation MyScrollView
- (void)setFrame:(CGRect)frame{
[super setFrame:frame];
[self setNeedsLayout];
}
- (void)layoutSubviews{
[super layoutSubviews];
NSArray * subviews=self.subviews;
for(UIView * view in subviews){
CGRect viewFrame=view.frame;
viewFrame.size.width=self.bounds.size.width;
view.frame=viewFrame;
}
}
#end
Using SnapKit it is even faster:
let helper = UIView()
scrollView.addSubview(helper)
helper.snp.makeConstraints { make in
make.width.equalTo(snp.width)
make.leading.trailing.top.equalToSuperview()
make.height.equalTo(0)
}

Resetting a fixed width and height in AutoLayout

I'm creating a custom UIView called CTCycleClock with a subview called CTCycleSlider. It reacts to a gesture so it can rotate on one axis (like looking from above upon a roulette table).
To achieve this, the main view CTCycleClock creates two constraints on the CTCycleSlider subview that center it on X and Y.
Example:
Furthermore, the CTCycleSlider subview creates two constraints on itself that set a specific width and height. This is necessary because otherwise upon rotation, the disk will make itself larger.
This works nicely and correctly. But when the superview has a bigger size (for instance on iPad), I don't know how to tell AutoLayout that the subview has a new fixed width and height equal to the superview.
This is how I set constraints in the superview:
NSLayoutConstraint *centerX = [NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1.f constant:0.f];
NSLayoutConstraint *centerY = [NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.f constant:0.f];
[self addConstraint:centerX];
[self addConstraint:centerY];
This is how I set constraints in the subview, where self.widthAndHeight is currently hardcoded to 320 on iPhone and 450 on iPad:
NSLayoutConstraint *w = [NSLayoutConstraint
constraintWithItem:self
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:self.widthAndHeight];
NSLayoutConstraint *h = [NSLayoutConstraint
constraintWithItem:self
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:self.widthAndHeight];
[self addConstraint:w];
[self addConstraint:h];
So my question is: how can I make a subview first hug the superview frame with a certain margin, but also set its width and height fixed?
EDIT: some clarifications as to why I need the constraint that sets width/height fixed.
When I won't set the width/height fixed, and the user touch-rotates the wheel, you get the following result:
In the above image, I've set constraints on the subview that set top/lead/width/height to the superview. That works great when the user hasn't rotated the wheel subview yet, but when they do, the autolayout constraints force the rectangular UIView smaller so it completely fits in the superview.
Thus the question remains: how can I create constraints that initially resize the subview correctly to the superview, but then set a fixed width/height so upon rotation, it stays the same size?
...how can I make a subview first hug the superview frame with a
certain margin, but also set its width and height fixed?
I don't understand your question. If you make your image view hug the superview with a fixed margin (on all sides) then the size of the image view is dictated by the superview.
You could pin the image view on 2 sides (e.g. top and left) and specify a size. Then the distance to the other 2 sides would vary based on the size of the superview. Or you could center it in the superview and fix the size, and then ALL The margins would vary based on the size of the superview.

iOS two views cover exactly half of parent view

In my app I want to achieve this layout:
So parent view contains two sub views. First one ends exactly in a middle (height / 2) and second starts in a middle of parent view. I have found out that it is impossible to do that in the IB with constraints. So I used this code in viewDidLoad method:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:firstView
attribute:NSLayoutAttributeHeight
relatedBy:0
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
Now it works but only if the app runs on the iPhone. Because size of the view is like iPhone screen. If this app runs on the iPad, there is a problem because screen has different size so this parent view is longer. And constraint (code above) still takes 0.5 * size of the views size from the IB and not size from the iPad size of the view. Item toItem:self.view still takes size from the IB.
Result is that this view has a same size in the iPad as in the iPhone. In the iPad there is a large blank space and then there is a view with iPhone size.
Can you tell what I have to do to make it universal for various screen sizes? Thank you very much
This is possible using constraints, but it is made a bit fiddly by IBs rather annoying and inflexible constraint manager. Here is how I managed it:
In IB, set the two views with the correct frames
Add an equal height constraint between the two views
Reduce the priority of any default height constraints on either of the views. Unfortunately IB does not let you remove these entirely, but setting them to anything less than 1000 will make sure they are ignored.
In the view controllers viewDidLoad method, add the constraint you already tried.
eg
- (void)viewDidLoad
{
[super viewDidLoad];
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.topView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
}
Thats it. Screengrabs of the IB constraints are shown below:
Try this code . It will set constraint value dynamically
In your .h file , implement this lines.
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
#define IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *TopsubviewheightConstraint;
Now create this constraint's as per given screen shot
connect TopsubviewheightConstraint height constraint from screen
implement this code in .m file
if (IS_IPHONE_5)
_TopSuperViewConstraint.constant = 275;
else if(IS_IPAD)
_TopSuperViewConstraint.constant = 502;
else
_TopSuperViewConstraint.constant = 230;
I hope it will help you.
you have 2 options.
create a second IB file for iPad
do everything by programm and use [[UIScreen mainScreen] bound]; instead of getting the sizes of parent ;)
I would do it without the constraints at all and set as follow:
// self.view is my container view
CGRect frame = [[UIScreen mainScreen] bound];
frame.size.height /= 2;
// upper View
upperView.frame = frame;
// lower View
frame.origin.y = frame.size.height;
// or alternatively
//frame.origin.y = CGRectGetMaxY(frame);
lowerView.frame = frame;
here you don't need any device specific options, everything is dynamic, bound to the size of your device's screen ;)
OK so I just figured out how to do this. Simply put the code into viewDidLayoutSubviews method and not to viewDidLoad. The solution I found in the topic Unable to set frame correctly before viewDidAppear.
Here is my code:
[subView1 setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height / 2)];
[subView2 setFrame:CGRectMake(0, self.view.frame.size.height / 2, self.view.frame.size.width, self.view.frame.size.height / 2)];
Thanks to all for effort!
Thanks to Tark's answer, I managed to to this using constraints as well:
Add Vertical Space constraint for TobView to Top Layout Guide (Using StoryBoard)
Add Vertical Space constraint for BottomView to Bottom Layout Guide (Using StoryBoard)
Add two height constraints for each view in ViewDidLoad
Code:
NSLayoutConstraint *constraint;
constraint = [NSLayoutConstraint constraintWithItem:_viewTop
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:_viewBottom
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
[self.view addConstraint:constraint];
You can do this with constrains (no code required!)
1.- First create two UIview and manually set it's height to half of the size of the current device, positioning one over the other, just like this:
2.- Next you must set the constraints for each one of them like this (this will allow to the container fill the whole screen, one over the another):
Top container
Bottom container
3.- Finally you must select both containers and add a new constrain that specify that they will have in the same height
(remember to clid "Add X Constrains" for each step)
now it should be ready to put the label inside each container, and you will ready

Resources