Constraints messing up frame size - ios

I use the following line of code to set the size of a button:
self.toolsButton.frame.size = CGSizeMake(190, 40)
All is fine, until I add the following layout constraint:
var constrainToCenter = NSLayoutConstraint(item: toolsButton, attribute: .CenterX, relatedBy: .Equal, toItem: self.view, attribute: .CenterX, multiplier: 1.0, constant: 0.0)
self.view.addConstraint(constrainToCenter)
As I understand it, this constraint code horizontally centers the button with the view, but why would that have an effect on the frame size? How can I maintain the frame size while also having the constraint?

You can add a height and a width constraint to enforce the size of the button.
If you are using constraints, it's best if your constraints completely define the size and position of the element.
When you add constraints for just one attribute, it can conflict with the default system constraints.
Edit - some more information:
If you create the view in code, the view.translatesAutoresizingMaskIntoConstraints attribute should default to YES.
This means that the system will automatically add constraints to it. And probably they conflict with the constraint you added manually.
So the best solution here, I think, is to set
view.translatesAutoresizingMaskIntoConstraints = NO
and then add 4 constraints manually:
horizontal position
vertical position
height
width
When you don't need to create any specific constraints, just leave that option to YES, the system will create constraints to enforce the frame you set and often that will be enough.

Related

Swift trailing constraint spacing depending on width of superview multiplied with a value (programmatically)

Let's say I have a UITableViewCell.
In the contentView of the cell, I have a subView. The trailing of the subView to the contentView is depending on the width of the contentView, multiplied with a value.
What I'm trying to achieve is:
let trailingSpacing: CGFloat = contentView.frame.size.width * 0.2
subView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -trailingSpacing).isActive = true
The piece of code above is what I was trying to do in layoutSubviews() of the UITableViewCell. It's not working and besides that, it doesn't feel right. Is there a more clean way to do this? I don't think you can do this in 1 constraint, but maybe I'm wrong.
The constraints are alle done in code, there is no storyboard/xib/nib.
Thanks in advance!
I'm not sure whether you're able to solve it with a single constraint, but you can add a spacer view to solve this. Anchor the spacer view to the trailing of your container, and the trailing of your "target" view to the leading of the spacer. Then, give the spacer a width that's a multiple of the container's width:
let constraint = NSLayoutConstraint(
item: spacer,
attribute: .width,
relatedBy: .equal,
toItem: container,
attribute: .width,
multiplier: 0.2, // <-- tweak this
constant: 0
)
container.addConstraint(constraint)
(You may need to switch space and container in the constraint, I always forget which is the one the multiplier is applied to.)
You can even do this solution in Interface Builder.

How to update Height of the view Programmatically?

One issue i am facing related to autolayouts. I am setting height of view containing image views to zero first via autolayouts. But if certain function is called I want that height updated to a constant value, but height of my view is not getting updated.
Here is the code, i have updated height programmatically inside the function but it is not working.
let heightContraint = NSLayoutConstraint(item: businessImageView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 40)
businessImageView.addConstraint(heightContraint)
First create IBOutlet of the height constraint.
You just need to change constant property of the constraint.
For e.g.:
self.consTblFilterHeight.constant = 100.0
self.view.layoutIfNeeded()
Replace self.view with the parent view of the view you are changing the height.
Create your constraint outlet and then set it like this :
self.heightConstraintOutlet.constant = newHeightValue
businessImageView.addConstraint(heightContraint) is not the code to update the constraint. It adds a constraint.
So as to update the height of parent view (which has images), you would need to update the constant for businessImageView's height constraint.
businessImageView.heightConstraint.constant = 40
self.view.layoutIfNeeded()
Approach
activate constraint
change constant value
Code
heightConstraint.isActive = true
heightConstraint.constant = 20

Adding vertical space constraint equal to superview height multiple

I want to achieve a very simple thing. I have a UIView, i want the vertical space between my UIView bottom and bottom layout guide to be 10% of the container height (in this case viewController.view). How can achieve this in storyboards?
So some thing like this
UIView.bottom = Height of superView * 0.1 + 0 from the Bottom layout guide
is there anyway to achieve this in storyboards. Currently i can just some constant magic number which will not work on iPhone 4s all the way till iPhone 6 plus.
Clicking on the constraint shows this properties, so how can i put something like superViewHeight * 0.1 in here. I understand that i can do this if i am setting the height of the view but how to do in this case.
Thanks
You need to invert the first and second item in this case. Simply click on first item dropdown and you will see the option.
Secondly, give a value of 0.9 in multiplier section. That will make the gap 10% of total height.
If I understand correctly, you want to create a constraint in proportion to the superview's height and not the height of the view itself.
You can do this programmatically by creating an NSLayoutConstraint and specify it's constant an run time.
let marginToBottomLayout = customView.superview!.frame.size.height * 0.1
let constraint = NSLayoutConstraint(item: customView,
attribute: .Bottom,
relatedBy: .Equal,
toItem: self.bottomLayoutGuide,
attribute: .Top, multiplier: 1.0,
constant: marginToBottomLayout)

How do I retrive the value of a constraint?

I have a NSLayoutConstraint constraint:
var myConstantLeading: CGFloat = 10
var myConstantTrailing: CGFloat = 10
var myConstraintLeading = NSLayoutConstraint (item: image,
attribute: NSLayoutAttribute.Leading,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Leading,
multiplier: 1,
constant: myConstantLeading)
self.view.addConstraint(myConstraintLeading)
var myConstraintTrailing = NSLayoutConstraint (item: image,
attribute: NSLayoutAttribute.Trailing,
relatedBy: NSLayoutRelation.Equal,
toItem: self.view,
attribute: NSLayoutAttribute.Trailing,
multiplier: 1,
constant: myConstantTrailing)
self.view.addConstraint(myConstraintTrailing)
When an UIButton is pressed, the image gets scaled:
self.image.transform = CGAffineTransformMakeScale(0.8, 0.8)
Though, after the transformation finishes, the constant doesn't change:
println(myConstraint.constant) // equals to 10
println(myConstant) // equals to 10
I resized the image, hence the constants should vary. Why isn't that happening?
The constraints you've set both use "Equal" relationships with a constant of 10pt from either side. If you change the image size without adjusting the constraints you've violated the constraint requirements & have an ambiguous layout.
If you want to observe the change in constant values they need to be allowed to change. You have to change "Equal" to "Greater Than Or Equal" so the constraint is allowed to vary from its current 10pt value. This of course assumes that the image can only shrink - it will never be larger than 10pt away from the edge, but it could be smaller.
You also still need to clearly define what you want to happen to the layout after transforming the image. If you want the image to remain centered after the button tap/resize, you would ideally just add a constraint to the image to center it horizontally in the container.
Adjusting only "=" to ">=" would still be ambiguous, because the system doesn't know how far from left or right the image should be, nor what the image width will be. You need to give it more information, such as "center horizontally in container" AND "leading & trailing edges >= 10pt from the superview".
If you look at UIView Class Reference transform property you see a warning stating :
If this property is not the identity transform, the value of the frame property is undefined and therefore should be ignored.
and since your view transform property is not the identity transform and its frame is undefined then all its constraints constants should be ignored as well because they are linked with view's frame

Auto Layout left margin constraint based on percentage of screen width

I have 4 buttons. I want them to be 13% of the screen width from the left edge and bottom edge. I'm using Auto Layout and Size Classes. I know I can specify a number of points in Interface Builder with Storyboards but obviously this won't get the job done when going from device to device. Do I need to hook up a constraint through an IBOutlet and calculate the constraint there in code to achieve the desired result? Or is this possible using Interface Builder?
This is special case, which can't be handled in IB (AFAIK). These combo boxes don't contain all available attributes.
Do it in code:
let con = NSLayoutConstraint(item: myView, attribute: .Left, relatedBy: .Equal,
toItem: fullWidthView, attribute: .Width, multiplier: 0.13, constant: 0.0)
Replace myView with your button. Replace fullWidthView with any view which occupies whole width of the device. Typically UIViewController.view.
And do the same thing with .Bottom, .Height and fullHeightView.

Resources