I'm trying to place my subview with a left margin based on the width of the parent view. This sounds simple but I can't figure out how to do it using autolayout.
Logically, I would only need to set the left margin value at a certain percentage value of the parent's width but at the moment, I fail to translate that logic to autolayout.
This is my code at the moment:
var view = UIView();
view.backgroundColor = UIColor.redColor();
view.frame = CGRectMake(0, 0, 320, 400);
var sview = UIView();
sview.setTranslatesAutoresizingMaskIntoConstraints(false);
sview.backgroundColor = UIColor.yellowColor();
//sview.frame = CGRectMake(0, 0, 50, 50);
view.addSubview(sview);
var dict = Dictionary<String, UIView>()
dict["box"] = sview;
var con1 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 20.0);
view.addConstraint(con1);
var con2 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Width, multiplier: 0.75, constant: 0.0);
view.addConstraint(con2);
var con3 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0.0);
view.addConstraint(con3);
var con4 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Height, multiplier: 1.0, constant: 0.0);
view.addConstraint(con4);
This is the where the code returns an error:
var con2 = NSLayoutConstraint(item: sview, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.Width, multiplier: 0.75, constant: 0.0);
view.addConstraint(con2);
Error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* +[NSLayoutConstraint constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:]: Invalid pairing of layout attributes'
Does anyone have any idea on how to achieve this? I just want the left margin to be 0.75% of the parent view's width.
Thanks.
Auto Layout defines your user interface using a series of constraints. Constraints typically represent a relationship between two views. Auto Layout then calculates the size and location of each view based on these constraints. This produces layouts that dynamically respond to both internal and external changes.
Multiplier is there for creating Proportional Constraint. Auto Layout calculates the first item's attribute to be the product of the second item's attribute and this multiplier . Any value other than 1 creates a proportional constraint.
The “Constrain to margins” checkbox determines whether constraints to the superview use the superview's margins or its edges. The lower portion of the popover lets you set the item's width or height. The Width and Height constraints default to the current canvas size, though you can type in different values.
What you want is the left of sview
to be at some point of the left of view
and you are writing that you want the left of sview
to be at some point of the width of view
which is not a correct pairing of layout attributes as your error says.
Here is what you need to do:
NSLayoutConstraint(item: sview,
attribute: NSLayoutAttribute.Left,
relatedBy: NSLayoutRelation.Equal,
toItem: view,
attribute: NSLayoutAttribute.Left,
multiplier: 1,
constant: (CGRectGetWidth(view.bounds) * 0.75));
Hope it helps!
EDIT
I found a great article about Percented based margins:
https://web.archive.org/web/20170624134422/http://simblestudios.com/blog/development/percentage-width-in-autolayout.html
Or even simpler:
https://web.archive.org/web/20170704113819/http://simblestudios.com/blog/development/easier-percentage-width-in-autolayout.html
It is possible to create a percentage-based margins with auto layout constraints between a subview and its superview. The margin will change dynamically as the size of the superview changes. For example, this is how to create a trailing 10% margin.
The is one problem with this manual approach though - it does not work in right-to-left language, Arabic, for example. Right-to-left layouts require a bit different settings for the constraint but we can not keep both in one storyboard.
Here is a library that I wrote that lets you create percentage-based constraints. It does handle the right-to-left language case.
https://github.com/exchangegroup/PercentageMargin
You can subclass NSLayoutConstraint
to accept a margin percentage via @IBInspectable
. Then subscribe to UIDeviceOrientationDidChangeNotification
to run the calculation and stuff into the constant
value so it is updated whenever the layout changes.
/// Layout constraint to calculate size based on multiplier.
class PercentLayoutConstraint: NSLayoutConstraint {
@IBInspectable var marginPercent: CGFloat = 0
var screenSize: (width: CGFloat, height: CGFloat) {
return (UIScreen.mainScreen().bounds.width, UIScreen.mainScreen().bounds.height)
}
override func awakeFromNib() {
super.awakeFromNib()
guard marginPercent > 0 else { return }
NSNotificationCenter.defaultCenter().addObserver(self,
selector: #selector(layoutDidChange),
name: UIDeviceOrientationDidChangeNotification,
object: nil)
}
/**
Re-calculate constant based on orientation and percentage.
*/
func layoutDidChange() {
guard marginPercent > 0 else { return }
switch firstAttribute {
case .Top, .TopMargin, .Bottom, .BottomMargin:
constant = screenSize.height * marginPercent
case .Leading, .LeadingMargin, .Trailing, .TrailingMargin:
constant = screenSize.width * marginPercent
default: break
}
}
deinit {
guard marginPercent > 0 else { return }
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}
First you specify the new subclass in Identity Inspector
:
Then you can use it like this:
The only caveat I can think of is the constants in the Storyboard are not used at runtime, but instead are overwritten with the percentage based calculation. So it does require some duplicate effort, once to actually layout the views on Storyboard based on points just so you get a sense of what the screen layout looks like, then percentages kick in at runtime.
For more details, check out this article: http://basememara.com/percentage-based-margin-using-autolayout-storyboard/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With