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
Related
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.
I am trying to layout some custom views and when I try to activate the constraints, Xcode says that I can't use multiplier. Here is an example of the code:
class MenuView: UIView {
var addButton: AddButton!
var settingsButton: SettingsButton!
// ........
func setConstraints (withBarReference reference: NSLayoutYAxisAnchor) {
NSLayoutConstraints.activateConstraints([
// ........
addButton.centerXAnchor.constraintEqualToAnchor(self.centerXAnchor, multiplier: 0.5),
// ........
settingsButton.centerXAnchor.constraintEqualToAnchor(self.centerXAnchor, multiplier: 1.5)
])
}
}
The thing here is that Xcode gives a syntax error on the contraintEqualToAnchor: functions and says that I should replace "multiplier" to "constant".
Why can't I use the multiplier option with the X center anchors?
You can't set multiplier using helper functions, but you can set multiplier using NSLayoutConstraint initializer. Just got stuck by this myself, but found the answer.
Your code: addButton.centerXAnchor.constraintEqualToAnchor(self.centerXAnchor, multiplier: 0.5)
Correct code: NSLayoutConstraint(item: addButton, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 0.5, constant: 0)
Also, don't forget to activate this constraint by typing isActive = true
Previous answers work very weird now.
You can simply create UILayoutGuide with multiplier width/height with view and set guide.trailing equal to the centerX of your subview.
For example, if you need to place the addButton in the first 1/3 of a view and settingsButton in 2/3 you can simply set two layout guides
let addButtonGuide = UILayoutGuide()
self.addLayoutGuide(addButtonGuide)
addButtonGuide.widthAnchor.constraint(equalTo: self.widthAnchor, multiplier: 1/3).isActive = true
addButtonGuide.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
addButton.centerXAnchor.constraint(equalTo: addButtonGuide.trailingAnchor).isActive = true
// same for settingsButton but using 2/3 for the multiplier
But the really best way is to use UIStackView and set its distribution property to equalCentering.
Another option is to use uncommon Auto Layout API to create NSLayoutDimension between two centerXAnchors and make constraint to self.widthAnchor:
addButton.centerXAnchor.anchorWithOffset(to: self.centerXAnchor)
.constraint(equalTo: self.widthAnchor, multiplier: 0.25).isActive = true
self.centerXAnchor.anchorWithOffset(to: settingsButton.centerXAnchor)
.constraint(equalTo: self.widthAnchor, multiplier: 0.25).isActive = true
It seems that in IB you can use the multiplier option with Center X and obtain the effect you're looking for (set the center of button1 at 1/4 the width of the view it's in, and the center of button2 at 2/3 of the width of the view it's in):
.
I tried to use it both in code and in IB, and in code I got the same error as you.
Surprisingly, in IB it worked, no errors, no warnings. (I am using Xcode 7, will try it in Xcode 8 to see if it still works).
You can't use multipliers on NSLayoutXAxisAnchor anchors - multiplying by a position along a line doesn't make sense in a way that the constraints system can understand. You can only use multipliers with NSLayoutDimension anchors, which measure lengths, like the width.
The layout you are trying to make would be better achieved using a stack view.
Just a very direct question, but we had spent many hours trying to find a working solution but faild.
In Xcode, storyboard, how to set a constraint so one view can be located 30% of total window height from the top of the superview? And we need it to be that way for ALL supported iOS devices of all orientations.
Please see my illustration attached.
I demonstrate this below - you just have to change the value of multiplier.
Update
Sorry, I have misunderstood your problem.
You'll need to add the constraints from code like so (the xConstraint is totally arbitrary, but you must need to define x, y positions, width and height, for an unambiguous layout):
#IBOutlet weak var imageView: UIImageView!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let yConstraint = NSLayoutConstraint(item: imageView, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1, constant: view.bounds.height / 3)
let xConstraint = NSLayoutConstraint(item: imageView, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1, constant: 30)
NSLayoutConstraint.activateConstraints([yConstraint, xConstraint])
}
This way, the equation will be:
imageView.top = 1 * view.top + (view.width / 3)
Original answer
Auto Layout uses the following equation for constraints:
aView.property = Multiplier * bView.property + Constant
Based on this, you can simply add an equal width/height constraint, then add a multiplier:
So the equation will be:
view.height = 0.3 * superView.height + 0
You should calculate it.
1. Calculate how many percents are from top to center ImageView
2. Set Vertical center to ImageView
3. Configure multiplier in Vertical center constraint and set multiplier from 1
For example: multiplier 0.5 will be 25% from top to center ImageView. So your multiplier will be ~0.6
By the way, there is another way how to do it:
1. Create transparent view from top to your imageView
2. Set height equal to your subview
3. Set multiplier to 0.3 to this height constraint
4. Set bottom space from your imageView to this transparent view equal to zero
In the Equal Heights Constraint properties pane, you set the multiplier to "1:3" (i.e. 30% in division notation).
To avoid having to recalculate a constant each time after layoutSubviews, use UILayoutGuide.
Create a layout guide equal to 30% of the view's height, and then use that to align the top of the child view. No manual layout calculations necessary.
// Create a layout guide aligned with the top edge of the parent, with a height equal to 30% of the parent
let verticalGuide = UILayoutGuide()
parent.addLayoutGuide(verticalGuide)
verticalGuide.topAnchor.constraint(equalTo: parent.topAnchor).isActive = true
verticalGuide.heightAnchor.constraint(equalTo: parent.heightAnchor, multiplier: 0.3).isActive = true
// Align the top of the child to the bottom of the guide
child.topAnchor.constraint(equalTo: verticalGuide.bottomAnchor).isActive = true
UILayoutGuide can be laid out with constraints like any view, but doesn't appear in the view hierarchy.
Here is Simple Solution if you want to give Constraint according to Screen.
To Set Height Percentage
To Set Width Percentage
import UIKit
extension NSLayoutConstraint{
/// Set Constant as per screen Width Percentage
#IBInspectable var widthPercentage:CGFloat {
set {
self.constant = (UIScreen.main.bounds.size.width * newValue)/100
}
get {
return self.constant
}
}
/// Set Constant as per screen Height Percentage
#IBInspectable var heightPercentage:CGFloat {
set {
self.constant = (UIScreen.main.bounds.size.height * newValue)/100
}
get {
return self.constant
}
}
}
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
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.