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).
Related
I'm trying to set a constraint where the width of two views should fill the screen. If I change the width of one view, the other view should change accordingly:
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-8-[view1]-8-[view2]-8-|" options:0 metrics:nil views:views]];
I think this is rather simple, but the code seems to ignore the size of view2. view1 keeps getting the entire width of the screen and setting the frame of view2 doesn't affect the width of view1.
What am I missing?
Assign a minimum size constraint to view2 as well, or "0 width" will be a valid solution to the constraints.
[NSLayoutConstraint activateConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-8-[view1(>=minSize)]-8-[view2(>=minSize)]-8-|"
options:0
metrics:#{#"minSize" : #50}
views:views]];
Also, don't use addConstraints: anymore (iOS 8+). Use -[NSLayoutConstraint setActive:] or +[NSLayoutConstraint activateConstraints:]
As for changing view2's size, you can't set views' frames directly when you're using Auto Layout. The value will be overwritten on the next layout pass. You should create an explicit width constraint for the view:
[NSLayoutConstraint constraintWithItem:view2
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:120];
and then adjust that constraint's constant when you need to change the view's size.
I would like to constraint the center of subview to be at 1/6 of the overall width of the superview.
For example:
If superview's width = 6
CenterX of subview = 1
I wrote the following code in the superview class (self), to constraint the centerX of aSubview, and it is crashing:
// Hits here
[NSLayoutConstraint constraintWithItem:self.aSubview
attribute:NSLayoutAttributeCenterX
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:1/6
constant:0];
// Crashes here
Is there a way to do this with NSLayoutConstraints at all?
I have two ideas why this might be crashing:
1) what is self in this context? Are you sure it's a UIView subclass?
2) 1/6 should result in 0, and that is not a valid multiplier. Try 1.0/6 instead
UPDATE:
method name is
+ constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:
where's the relatedBy: part in your code?
UPDATE 2:
It seems it's not allowed after all. I tried to reproduce the crash and it logs the following error:
Invalid pairing of layout attributes
But! You can use Trailing instead of Width to achieve desired layout, it holds the same value actually, if the superview's left side is connected to the screen (see image to understand better).
This is tested and working:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.aView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTrailing
multiplier:1.0/2
constant:0];
[self.view addConstraint:constraint];
be sure to add your constraint to the superview of the view you want to position.
Create a spacer view. Set its hidden to YES. (Hidden views still participate in layout.) Pin the spacer's leading edge to the superview's leading edge. Constrain the spacer's width to be 1/6 of the superview's width.
Then constrain the “centered” view's center to the spacer view's trailing edge.
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)
}
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.
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];
}