UIImageView and Auto Layout in Code - ios

I'm trying to use programmatic visual constraints to display a label and a button next to one another. However, the UIImageView used as the button's background is making the intrinsic size of the button much too large.
I attempted to add a constraint that forces the height of the button to match the height of the label. But I just got a super tall label instead of a smaller button.
How do I set a constraint so that the button height is the same height as the label (and not vice-versa)
The button should keep the original aspect ratio of the image - its width should also match its own height (maybe this comes for free?)

The following works for this:
Set a width for the button in the visual layout: #"|-[titleLabel][refreshButton(==26)]"
Add a constraint such that the height of the button is equal to its own (now explicit) width:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:refreshButton
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:refreshButton
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:0.0f];
I would still prefer a solution that uses the label's height, instead of a fixed value.

Related

Interface Builder Constraints

I'm trying to create layout like this in interface builder.
I'm using constraints to make these views stretchable.
Top view has left, right and top space bound to superview, and bottom space to bottom view.
Bottom view has the left right and bottom space bound to superview.
On runtime I add ViewController views to both of them.
Issue here is that there is no constraint for Y of bottom view, IB shows red error arrow and so on. That is because I don't know exact height of it. Is there any "android wrap_content" constraint for yellow view to be with height which is equal to it's inner view added in runtime?
Add a constraint for the height of the yellow view. Then add an IBOutlet for that constraint so you can make changes to the constraint's constant value at runtime. If you need to calculate the size that satisfies the yellow view's constraints, you can use systemLayoutSizeFittingSize:.
Alternatively, you can rely on the yellow view's intrinsicContentSize. Add a placeholder Intrinsic Size for the view in Interface Builder. If your yellow view implements intrinsicContentSize or has constraints that give it an unambiguous height (e.g. if it contains a UILabel or UIImageView that is pinned at the top and the bottom), then that will be enough to appropriately size the view.
Just add height constraint to yellow view, for example:
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:self.yellowView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeHeight
multiplier:0.20f
constant:0.0f];
heightConstraint.active = YES;

autolayout centering uilabel on x-axis fit to content with uiimage follow on the left side

I have a UILabel which should be centered horizontally and the width should be set according to its content length. and on the left side of the UILabel an UIImage should be positioned which should be aligned to UILabel. if UILabel needs more space then it should push UIImage to the left, and if UILabel needs less space then it should pull UIImage toward x-center.
I had it without layout working fine, but has to use auto layout. I'm trying but i can't figured it out.
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-padding-[img(16)][lblUserName]-padding-|" options:0 metrics:#{#"padding":[NSNumber numberWithFloat:Padding]} views:displayViewDic];
is it possible with auto layout? so sometimes it will be like in number 1 and other times like number 2.
#"H:|-padding-[img(16)][lblUserName]-padding-|"
Here you're saying that the image has to be a fixed distance from the superview's leading edge. That doesn't match your description.
You might just need to change it to
#"H:|-(>=padding)-[img(16)][lblUserName]-(>=padding)-|"
To allow some flexibility in the margins.
To center a view horizontally, you have to manually create the constraint:
[view addConstraint:[NSLayoutConstraint constraintWithItem:lblUserName
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0]];
You don't need to use sizeToFit or any other methods like that - an image view and a label will have an intrinsic content size based on the image or the text.
Because you have an inequality, you may need to force the label to be as narrow as possible to prevent stretching:
[lblUserName setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
Add a horizontal center constraint to the label. Just this, and a suitable y position constraint would keep the label in the center. It'd expand equally in both directions to accommodate the content.
Now, add a horizontal spacing constraint to the image view's trailing space and the label's leading space for the x position, a suitable constraint for the y position (align vertical center with the label, perhaps?) and suitable constraints/image/intrinsic size for the size.
Code:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[img(width)]-padding-[lblUserName]" options:0 metrics:#{#"width": 50, #"padding": 20} views:displayViewDic]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:lblUserName attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:superview attribute:NSLayoutAttributeCenterX multiplier:0.0 constant:0.0]];

UILabel Autolayout width trouble

So I'm trying to use autolayout for cell content view to get the proper layout. So my problem is that I have a UILabel that changes its size with respect to its text and I also have a UIView as a background view for this label with rounded corners. So my question is, how to force this UIView's width to be 10 points wider than the UILabel. I managed to make it the same width but how can I force it always to be a certain length wider?
Thank you in advance!
NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:yourLabel
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:yourLabel.superview
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:10]; // <-- this
[yourLabel.superview addConstraint:widthConstraint];
An autolayout constraint is nothing but an equation of the form
attribute1 == multiplier × attribute2 + constant
Note that programatically, you can virtually set any constraint on your views. The interface builder is however a bit limited given that you can relate only certain pairs of (attribute1,attribute2) an as you have noticed you may not be able to provide constant.
Have a look at
https://developer.apple.com/library/ios/DOCUMENTATION/AppKit/Reference/NSLayoutConstraint_Class/NSLayoutConstraint/NSLayoutConstraint.html

AutoLayout issue with resizing subviews

I'm trying to figure out how to use Autolayout and I've founded a problem. I want to create a view in IB with size 200x200. This view, called them PieView, has two UIImageViews with frames (0, 0, 200, 200), for both of them.
My question is, how to override updateConstraints in code (I like visual format language), or in IB, that if I change size of my PieView (for example to 100), and subviews will changed too (0, 0, 100, 100).
And how can I change the size of PieView, I'm trying for width and for height
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:100.f];//kDiameter * sizeCoef];
If the subviews of the container view (PieView) are correctly positioned/sized/pinned relative to their container, all you need to do is update the width of the container view (PieView).
To change the view's size in code, you'll need to make sure you keep a reference (in a property, for example) to the constraint on the view's width. So if you added that constraint in Xcode, that means connecting an outlet for that constraint. Or if you added it in code (as you have written in your question), just assign the constraint to a property instead of a local variable.
Then, in updateConstraints, change the constant property of the constraint to the new width. Here's an example:
- (void)updateConstraints
{
// Probably want to wrap the below line with a check for when you should actually do this change,
// as updateConstraints may get called more than once (including when you aren't ready to change the width).
self.widthConstraint.constant = 100.0f;
}
Then just call setNeedsUpdateConstraints on the view when you're ready to change its width!
I'm not sure that you even need to re-configure the height or width constraints for the image views. If you want the image views to grow and shrink with their superview, then just pin the the sides of the images views to the superview in IB. You would not need to override updateConstraints.

Matching subview's width to it's superview using autolayout

Goal:
Have a UIWebView be the same width as it's superview, which is a UIScrollView, using autolayout constraints.
Code
NSLayoutConstraint *makeWidthTheSameAsScrollView =[NSLayoutConstraint
constraintWithItem:self.questionWebView
attribute:NSLayoutAttributeWidth
relatedBy:0
toItem:self.masterScrollView
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0];
[self.view addConstraint:makeWidthTheSameAsScrollView];
NSLog(#"The width of questionWebView *AFTER* adding the constrain is: %f", self.questionWebView.frame.size.width);
NSLog(#"The width of scrollView *AFTER* adding the constrain is: %f", self.masterScrollView.frame.size.width);
Current Result
When I log the width of self.questionWebView (the UIWebView), it's width does not change when the autolayout constrain is applied.
Questions
Is this the correct approach?
What am I doing wrong?
p.s I know it is against Apple's recommendations to place a UIWebView in a UIScrollView, however I've turned off the ability to scroll the UIWebView using the property self.questionWebView.userInteractionEnabled = NO;. And currently using a UIWebView is my best strategy for displaying an HTML table.
Improving on Rob's answer, as requested.
As Rob already mentioned, UIScrollViews have peculiar behavior under Auto Layout.
What is of interest in this case is the fact that the scrollView total width is determined by using its subviews total width. So while the scrollView already asks the webView for its width, you're telling the webView to also ask the scrollView for its width. That's why it doesn't work. One is asking another, and no one knows the answer. You need another reference view to use as a constraint for the webView, and then the scrollView will also be able to successfully ask about its expected width.
An easy way this could be done: create another view, containerView, and add the scrollView as a subview to that. Then set the proper constraints for containerView. Let's say you wanted the scrollView centered on a viewController, with some padding on the edges. So do it for the containerView:
NSDictionary *dict = NSDictionaryOfVariableBindings(containerView);
[self.view addConstraints:[NSLayoutConstraints constraintsWithVisualFormat:#"H|-(100)-[containerView]-(100)-|" options:0 metrics:0 views:dict];
[self.view addConstraints:[NSLayoutConstraints constraintsWithVisualFormat:#"V|-(100)-[containerView]-(100)-|" options:0 metrics:0 views:dict];
Then you can proceed adding the webView as a subview to the scrollView and setting its width:
NSLayoutConstraint *makeWidthTheSameAsScrollView =[NSLayoutConstraint
constraintWithItem:self.questionWebView
attribute:NSLayoutAttributeWidth
relatedBy:0
toItem:containerView
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0];
[self.view addConstraint:makeWidthTheSameAsScrollView];
This would make the scrollview as large and tall as the webView, and they both would be placed as intended (with the constraints set on containerView).
Scrollviews are a bit strange in how they interact with auto layout. See TN2154 (UIScrollView and Autolayout).
See also UIScrollView doesn't use autolayout constraints.
In general, you need to get the width of the contained view some other way than "the current width of the scrollview" since in auto layout the scrollview's width (i.e. content width) is defined in terms of its content. Thus your current request is circular.

Resources