Adding vertical space constraint equal to superview height multiple - ios

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)

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.

Programmed Slider Constraint is Not Updating

I have am attempting to learn how to populate a view in my storyboard with sliders and buttons, programmatically. I am trying, currently, to get one programmed slider to adhere to a programmed NSLayoutConstraint
Here is my code:
let centerXConstraint = NSLayoutConstraint(item: self.volumeSliderP, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.centerX, multiplier: 1.0, constant: 10.0)
self.view.addConstraint(centerXConstraint)
I should mention, that when I substitute the first item for a slider which already exists on the view (which was placed via Storyboard, with it's own constraints also placed with IB/Storyboard), it does updated correctly with the above NSLayoutConstraint code. Also, I have been able to update my programmed volumeSliderP with custom code to change it's handle and rotate it to vertical successfully.
What step am I missing to allow this NSLayoutConstraint code to work upon my programmed slider?
Thank you for any help!
When working with constraints in code, you need to do two (maybe three) things, regardless of control type:
Set the translatesAutoresizingMaskIntoConstraints to false.
Failure to do so will set off constraint conflicts, which will appear in the console log. I usually create an extension to UIView for this:
public func turnOffAutoResizing() {
self.translatesAutoresizingMaskIntoConstraints = false
for view in self.subviews as [UIView] {
view.translatesAutoresizingMaskIntoConstraints = false
}
}
Then in viewDidLoad (after adding my subviews) I simply add a line:
view.turnOffAutoResizing()
Consider if any subviews have intrinsic content size.
As explained in the linked Apple doc, if you have label and a text field, the text field will expand to fit the label without the need for setting widths. A UISlider does not have an intrinsic width but it does have an intrinsic height.
So in your case you need to not only set position, it needs to define the width.
A combination of top and leading will yield enough for the layout engine to know "where" and "height", but not "width". Same would go if you defined "centerX" and something - you didn't list any code - for the Y factor (top, bottom, centerY).
If I'm stating this clearly, you should be able to see that the engine will know enough to say (in frame coordinates) "start the slider at X/Y, height is XX points (it has intrinsic height), but how long should it be?"
I typically set either top, leading, and trailing... or top, centerX, and width. But it varies with the need.

How to set a constraint using percentage?

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
}
}
}

Constraints messing up frame size

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.

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