Center UIView without overlapping neighbors views of different size? - ios

What I wish to achieve, the three of them are UILabels:
Spec:
left and right are edged UIViews.
middle is centered, even though side views are not the same size.
On collision between middle and left or middle and right, the text in middle gets tail truncated while left and right do not change.
Problem:
I cannot have meet both requirements (2) and (3) at the same time.
(2) is achieved with middle.autoAlignAxisToSuperviewAxis(.Vertical)
problem: no truncate, it just overlaps leftLabel and rightLabel.
(3) is achieved with middle.autoPinEdge(.Left, toEdge: .Right, ofView: leftLabel) and same strategy for the rightLabel.
problem: middle is centered in the area between leftLabel and rightLabel, if the last two do not have the same size, middle is not superview centered anymore.
Both applied create a constraint conflict and I don't know how to fix it, hence my question:
How can I center a UILabel and truncate it to avoir overlaps with side views of different sizes?

All you need is adding two constraints, one is horizontal spacing between middle and left, set the relation to 'Greater than or Equal', and constant for example 10; another is horizontal spacing between middle and right, the same configuration like constraint one.
When the text of middle label is too long, the constraints below will prevent middle from overlapping with label left and right.
Add sample code:
I suppose you had set the constraints to UILabel left and right in storyboard or in code. The sample below is used to configu UILabel middle
func setupConstraint() {
self.middle = UILabel()
self.middle!.backgroundColor = UIColor.yellowColor()
self.view.addSubview(self.middle!)
self.middle!.numberOfLines = 0
self.middle!.translatesAutoresizingMaskIntoConstraints = false
let middle = self.middle!
var constraint = NSLayoutConstraint(item: middle, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: left, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0)
self.view.addConstraint(constraint)
constraint = NSLayoutConstraint(item: middle, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.GreaterThanOrEqual, toItem: left, attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 10)
self.view.addConstraint(constraint)
constraint = NSLayoutConstraint(item: right, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.GreaterThanOrEqual, toItem: middle, attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 10)
constraint.priority = 1000
self.view.addConstraint(constraint)
constraint = NSLayoutConstraint(item: middle, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0)
self.view.addConstraint(constraint)
constraint = NSLayoutConstraint(item: middle, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.GreaterThanOrEqual, toItem: middle, attribute: NSLayoutAttribute.Height, multiplier: 1, constant: 100)
self.view.addConstraint(constraint)
self.middle!.text = "self.middle.translatesAutoresizingMaskIntoConstraints = false"
}
Sanpshot

i tried to set up a solution in a playground:
import UIKit
import XCPlayground
let viewController = UIViewController()
XCPlaygroundPage.currentPage.liveView = viewController.view
viewController.view.backgroundColor = UIColor.whiteColor()
let leftView = UIView()
leftView.translatesAutoresizingMaskIntoConstraints = false
leftView.backgroundColor = UIColor.greenColor()
let leftLabel = UILabel()
leftLabel.translatesAutoresizingMaskIntoConstraints = false
leftLabel.text = "leftleftleftleftleftleftleftleftleftleftleftleftleftleft"
leftView.addSubview(leftLabel)
NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-2-[leftLabel]-2-|", options: [], metrics: nil, views: ["leftLabel": leftLabel]))
NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-2-[leftLabel]-2-|", options: [], metrics: nil, views: ["leftLabel": leftLabel]))
let middleView = UIView()
middleView.translatesAutoresizingMaskIntoConstraints = false
middleView.backgroundColor = UIColor.redColor()
let middleLabel = UILabel()
middleLabel.translatesAutoresizingMaskIntoConstraints = false
middleLabel.setContentCompressionResistancePriority(UILayoutPriorityDefaultLow, forAxis: .Horizontal)
middleLabel.text = "middle"
middleView.addSubview(middleLabel)
NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-2-[middleLabel]-2-|", options: [], metrics: nil, views: ["middleLabel": middleLabel]))
NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-2-[middleLabel]-2-|", options: [], metrics: nil, views: ["middleLabel": middleLabel]))
let rightView = UIView()
rightView.translatesAutoresizingMaskIntoConstraints = false
rightView.backgroundColor = UIColor.greenColor()
let rightLabel = UILabel()
rightLabel.translatesAutoresizingMaskIntoConstraints = false
rightLabel.text = "right"
rightView.addSubview(rightLabel)
NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-2-[rightLabel]-2-|", options: [], metrics: nil, views: ["rightLabel": rightLabel]))
NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-2-[rightLabel]-2-|", options: [], metrics: nil, views: ["rightLabel": rightLabel]))
viewController.view.addSubview(leftView)
viewController.view.addSubview(middleView)
viewController.view.addSubview(rightView)
NSLayoutConstraint(item: middleView, attribute: .CenterX, relatedBy: .Equal, toItem: viewController.view, attribute: .CenterX, multiplier: 1, constant: 0).active = true
NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|[leftView]->=8-[middleView]->=8-[rightView]|", options: [.AlignAllTop, .AlignAllBottom], metrics: nil, views: ["leftView": leftView, "middleView": middleView, "rightView": rightView]))
NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[leftView]", options: [], metrics: nil, views: ["leftView": leftView]))
results in something like this:
UPDATE (without embedding the labels in their own views):
import UIKit
import XCPlayground
let viewController = UIViewController()
XCPlaygroundPage.currentPage.liveView = viewController.view
viewController.view.backgroundColor = UIColor.whiteColor()
let leftLabel = UILabel()
leftLabel.translatesAutoresizingMaskIntoConstraints = false
leftLabel.backgroundColor = UIColor.greenColor()
leftLabel.text = "leftleftleftleftleftleftleftleftleftleftleftleftleftleft"
let middleLabel = UILabel()
middleLabel.translatesAutoresizingMaskIntoConstraints = false
middleLabel.setContentCompressionResistancePriority(UILayoutPriorityDefaultLow, forAxis: .Horizontal)
middleLabel.backgroundColor = UIColor.redColor()
middleLabel.text = "middle"
let rightLabel = UILabel()
rightLabel.translatesAutoresizingMaskIntoConstraints = false
rightLabel.backgroundColor = UIColor.greenColor()
rightLabel.text = "right"
viewController.view.addSubview(leftLabel)
viewController.view.addSubview(middleLabel)
viewController.view.addSubview(rightLabel)
NSLayoutConstraint(item: middleLabel, attribute: .CenterX, relatedBy: .Equal, toItem: viewController.view, attribute: .CenterX, multiplier: 1, constant: 0).active = true
NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|[leftLabel]->=8-[middleLabel]->=8-[rightLabel]|", options: [.AlignAllTop, .AlignAllBottom], metrics: nil, views: ["leftLabel": leftLabel, "middleLabel": middleLabel, "rightLabel": rightLabel]))
NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[leftLabel]", options: [], metrics: nil, views: ["leftLabel": leftLabel]))
hope it helps :)

Related

How to make Constraints programmatically in iOS Swift 3.1? [duplicate]

I'm trying to figure this out since last week without going any step further. Ok, so I need to apply some constraints programmatically in Swift to a UIView using this code:
var new_view:UIView! = UIView(frame: CGRectMake(0, 0, 100, 100));
new_view.backgroundColor = UIColor.redColor();
view.addSubview(new_view);
var constX:NSLayoutConstraint = NSLayoutConstraint(item: new_view, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0);
self.view.addConstraint(constX);
var constY:NSLayoutConstraint = NSLayoutConstraint(item: new_view, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0);
self.view.addConstraint(constY);
var constW:NSLayoutConstraint = NSLayoutConstraint(item: new_view, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: new_view, attribute: NSLayoutAttribute.Width, multiplier: 1, constant: 0);
self.view.addConstraint(constW);
var constH:NSLayoutConstraint = NSLayoutConstraint(item: new_view, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: new_view, attribute: NSLayoutAttribute.Height, multiplier: 1, constant: 0);
self.view.addConstraint(constH);
But Xcode returns this weird output:
2014-10-03 09:48:12.657 Test[35088:2454916] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x7fa4ea446830 UIView:0x7fa4ea429290.centerX == UIView:0x7fa4ea4470f0.centerX>",
"<NSAutoresizingMaskLayoutConstraint:0x7fa4ea4516c0 h=--& v=--& UIView:0x7fa4ea429290.midX == + 50>",
"<NSLayoutConstraint:0x7fa4ea452830 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7fa4ea4470f0(375)]>",
"<NSAutoresizingMaskLayoutConstraint:0x7fa4ea446db0 h=-&- v=-&- 'UIView-Encapsulated-Layout-Left' H:|-(0)-[UIView:0x7fa4ea4470f0] (Names: '|':UIWindow:0x7fa4ea444b20 )>"
)
Will attempt to recover by breaking constraint <NSLayoutConstraint:0x7fa4ea446830 UIView:0x7fa4ea429290.centerX == UIView:0x7fa4ea4470f0.centerX>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in
<UIKit/UIView.h> may also be helpful.
2014-10-03 09:48:12.658 Test[35088:2454916] Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x7fa4ea44d160 UIView:0x7fa4ea429290.centerY == UIView:0x7fa4ea4470f0.centerY>",
"<NSAutoresizingMaskLayoutConstraint:0x7fa4ea451b30 h=--& v=--& UIView:0x7fa4ea429290.midY == + 50>",
"<NSLayoutConstraint:0x7fa4ea44cf00 'UIView-Encapsulated-Layout-Height' V:[UIView:0x7fa4ea4470f0(667)]>",
"<NSAutoresizingMaskLayoutConstraint:0x7fa4ea452700 h=-&- v=-&- 'UIView-Encapsulated-Layout-Top' V:|-(0)-[UIView:0x7fa4ea4470f0] (Names: '|':UIWindow:0x7fa4ea444b20 )>"
)
Will attempt to recover by breaking constraint <NSLayoutConstraint:0x7fa4ea44d160 UIView:0x7fa4ea429290.centerY == UIView:0x7fa4ea4470f0.centerY>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Can you help me?
Thanks a lot
Do you plan to have a squared UIView of width: 100 and Height: 100 centered inside the UIView of an UIViewController? If so, you may try one of the 6 following Auto Layout styles (Swift 5 / iOS 12.2):
1. Using NSLayoutConstraint initializer
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerY, multiplier: 1, constant: 0)
let widthConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 100)
let heightConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 100)
view.addConstraints([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerY, multiplier: 1, constant: 0)
let widthConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 100)
let heightConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 100)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerY, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 100).isActive = true
NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.height, relatedBy: NSLayoutConstraint.Relation.equal, toItem: nil, attribute: NSLayoutConstraint.Attribute.notAnAttribute, multiplier: 1, constant: 100).isActive = true
}
2. Using Visual Format Language
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let views = ["view": view!, "newView": newView]
let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:[view]-(<=0)-[newView(100)]", options: NSLayoutConstraint.FormatOptions.alignAllCenterY, metrics: nil, views: views)
let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[view]-(<=0)-[newView(100)]", options: NSLayoutConstraint.FormatOptions.alignAllCenterX, metrics: nil, views: views)
view.addConstraints(horizontalConstraints)
view.addConstraints(verticalConstraints)
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let views = ["view": view!, "newView": newView]
let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:[view]-(<=0)-[newView(100)]", options: NSLayoutConstraint.FormatOptions.alignAllCenterY, metrics: nil, views: views)
let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[view]-(<=0)-[newView(100)]", options: NSLayoutConstraint.FormatOptions.alignAllCenterX, metrics: nil, views: views)
NSLayoutConstraint.activate(horizontalConstraints)
NSLayoutConstraint.activate(verticalConstraints)
}
3. Using a mix of NSLayoutConstraint initializer and Visual Format Language
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let views = ["newView": newView]
let widthConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:[newView(100)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: views)
let heightConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[newView(100)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: views)
let horizontalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerY, multiplier: 1, constant: 0)
view.addConstraints(widthConstraints)
view.addConstraints(heightConstraints)
view.addConstraints([horizontalConstraint, verticalConstraint])
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let views = ["newView": newView]
let widthConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:[newView(100)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: views)
let heightConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[newView(100)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: views)
let horizontalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerY, multiplier: 1, constant: 0)
NSLayoutConstraint.activate(widthConstraints)
NSLayoutConstraint.activate(heightConstraints)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint])
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let views = ["newView": newView]
let widthConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:[newView(100)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: views)
let heightConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[newView(100)]", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: views)
NSLayoutConstraint.activate(widthConstraints)
NSLayoutConstraint.activate(heightConstraints)
NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerX, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: newView, attribute: NSLayoutConstraint.Attribute.centerY, relatedBy: NSLayoutConstraint.Relation.equal, toItem: view, attribute: NSLayoutConstraint.Attribute.centerY, multiplier: 1, constant: 0).isActive = true
}
4. Using UIView.AutoresizingMask
Note: Springs and Struts will be translated into corresponding auto layout constraints at runtime.
override func viewDidLoad() {
let newView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = true
newView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
newView.autoresizingMask = [UIView.AutoresizingMask.flexibleLeftMargin, UIView.AutoresizingMask.flexibleRightMargin, UIView.AutoresizingMask.flexibleTopMargin, UIView.AutoresizingMask.flexibleBottomMargin]
}
5. Using NSLayoutAnchor
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = newView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let verticalConstraint = newView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
let widthConstraint = newView.widthAnchor.constraint(equalToConstant: 100)
let heightConstraint = newView.heightAnchor.constraint(equalToConstant: 100)
view.addConstraints([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = newView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let verticalConstraint = newView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
let widthConstraint = newView.widthAnchor.constraint(equalToConstant: 100)
let heightConstraint = newView.heightAnchor.constraint(equalToConstant: 100)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint, widthConstraint, heightConstraint])
}
override func viewDidLoad() {
let newView = UIView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
newView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
newView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
newView.widthAnchor.constraint(equalToConstant: 100).isActive = true
newView.heightAnchor.constraint(equalToConstant: 100).isActive = true
}
6. Using intrinsicContentSize and NSLayoutAnchor
import UIKit
class CustomView: UIView {
override var intrinsicContentSize: CGSize {
return CGSize(width: 100, height: 100)
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
let newView = CustomView()
newView.backgroundColor = UIColor.red
view.addSubview(newView)
newView.translatesAutoresizingMaskIntoConstraints = false
let horizontalConstraint = newView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
let verticalConstraint = newView.centerYAnchor.constraint(equalTo: view.centerYAnchor)
NSLayoutConstraint.activate([horizontalConstraint, verticalConstraint])
}
}
Result:
It helps me to learn visually, so this is a supplemental answer.
Boilerplate code
override func viewDidLoad() {
super.viewDidLoad()
let myView = UIView()
myView.backgroundColor = UIColor.blue
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)
// Add constraints code here
// ...
}
Each of the following examples are independent of the others.
Pin left edge
myView.leading = leadingMargin + 20
Method 1: Anchor Style
let margins = view.layoutMarginsGuide
myView.leadingAnchor.constraint(equalTo: margins.leadingAnchor, constant: 20).isActive = true
In addition to leadingAnchor, there is also trailingAnchor, topAnchor, and bottomAnchor.
Method 2: NSLayoutConstraint Style
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.leadingMargin, multiplier: 1.0, constant: 20.0).isActive = true
In addition to .leading there is also .trailing, .top, and .bottom.
In addition to .leadingMargin there is also .trailingMargin, .topMargin, and .bottomMargin.
Set Width and Height
width = 200
height = 100
Method 1: Anchor Style
myView.widthAnchor.constraint(equalToConstant: 200).isActive = true
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true
Method 2: NSLayoutConstraint Style
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 200).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100).isActive = true
Center in container
myView.centerX = centerX
myView.centerY = centerY
Method 1: Anchor Style
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
Method 2: NSLayoutConstraint Style
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0).isActive = true
Notes
Anchor style is the preferred method over NSLayoutConstraint Style, however it is only available from iOS 9, so if you are supporting iOS 8 then you should still use NSLayoutConstraint Style.
The examples above showed just the one or two constraints that were being focused on. However, in order to properly place myView in my test project I needed to have four constraints.
Further Reading
Programmatically Creating Constraints documentation
If you want to fill your super view then I suggest the swifty way:
view.translatesAutoresizingMaskIntoConstraints = false
let attributes: [NSLayoutAttribute] = [.top, .bottom, .right, .left]
NSLayoutConstraint.activate(attributes.map {
NSLayoutConstraint(item: view, attribute: $0, relatedBy: .equal, toItem: view.superview, attribute: $0, multiplier: 1, constant: 0)
})
Other wise if you need non equal constraints check out NSLayoutAnchor as of iOS 9. Its often much easier to read that using NSLayoutConstraint directly:
view.translatesAutoresizingMaskIntoConstraints = false
view.topAnchor.constraint(equalTo: view.superview!.topAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: view.superview!.bottomAnchor).isActive = true
view.leadingAnchor.constraint(equalTo: view.superview!.leadingAnchor, constant: 10).isActive = true
view.trailingAnchor.constraint(equalTo: view.superview!.trailingAnchor, constant: 10).isActive = true
We can easily do this with in swift 5.1
setup 1
subview align to view center
subview width height set using float
view.addSubview(myView1)
myView1.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
myView1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
myView1.centerYAnchor.constraint(equalTo: view.centerYAnchor),
myView1.widthAnchor.constraint(equalToConstant: 100),
myView1.heightAnchor.constraint(equalToConstant: 100),
])
setup 2
subview align to view leading and top anchor
subview width set using view width height
view.addSubview(myView2)
myView2.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
myView2.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant: 16),
myView2.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor,constant: 16),
myView2.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.3),
myView2.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3)
])
Constraints for multiple views in playground.
swift 3+
var yellowView: UIView!
var redView: UIView!
override func loadView() {
// UI
let view = UIView()
view.backgroundColor = .white
yellowView = UIView()
yellowView.backgroundColor = .yellow
view.addSubview(yellowView)
redView = UIView()
redView.backgroundColor = .red
view.addSubview(redView)
// Layout
redView.translatesAutoresizingMaskIntoConstraints = false
yellowView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
yellowView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20),
yellowView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
yellowView.widthAnchor.constraint(equalToConstant: 80),
yellowView.heightAnchor.constraint(equalToConstant: 80),
redView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20),
redView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -20),
redView.widthAnchor.constraint(equalToConstant: 80),
redView.heightAnchor.constraint(equalToConstant: 80)
])
self.view = view
}
In my opinion xcode playground is the best place for learning adding
constraints programmatically.
Basically it involved 3 steps
fileprivate func setupName() {
lblName.text = "Hello world"
// Step 1
lblName.translatesAutoresizingMaskIntoConstraints = false
//Step 2
self.view.addSubview(lblName)
//Step 3
NSLayoutConstraint.activate([
lblName.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
lblName.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)
])
}
This puts label "hello world" in center of screen.
Please refer link Autolayout constraints programmatically
The problem, as the error message suggests, is that you have constraints of type NSAutoresizingMaskLayoutConstraints that conflict with your explicit constraints, because new_view.translatesAutoresizingMaskIntoConstraints is set to true.
This is the default setting for views you create in code. You can turn it off like this:
var new_view:UIView! = UIView(frame: CGRectMake(0, 0, 100, 100))
new_view.translatesAutoresizingMaskIntoConstraints = false
Also, your width and height constraints are weird. If you want the view to have a constant width, this is the proper way:
new_view.addConstraint(NSLayoutConstraint(
item:new_view, attribute:NSLayoutAttribute.Width,
relatedBy:NSLayoutRelation.Equal,
toItem:nil, attribute:NSLayoutAttribute.NotAnAttribute,
multiplier:0, constant:100))
(Replace 100 by the width you want it to have.)
If your deployment target is iOS 9.0 or later, you can use this shorter code:
new_view.widthAnchor.constraintEqualToConstant(100).active = true
Anyway, for a layout like this (fixed size and centered in parent view), it would be simpler to use the autoresizing mask and let the system translate the mask into constraints:
var new_view:UIView! = UIView(frame: CGRectMake(0, 0, 100, 100))
new_view.backgroundColor = UIColor.redColor();
view.addSubview(new_view);
// This is the default setting but be explicit anyway...
new_view.translatesAutoresizingMaskIntoConstraints = true
new_view.autoresizingMask = [ .FlexibleTopMargin, .FlexibleBottomMargin,
.FlexibleLeftMargin, .FlexibleRightMargin ]
new_view.center = CGPointMake(view.bounds.midX, view.bounds.midY)
Note that using autoresizing is perfectly legitimate even when you're also using autolayout. (UIKit still uses autoresizing in lots of places internally.) The problem is that it's difficult to apply additional constraints to a view that is using autoresizing.
Updated for Swift 3
import UIKit
class ViewController: UIViewController {
let redView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .red
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupAutoLayout()
}
func setupViews() {
view.backgroundColor = .white
view.addSubview(redView)
}
func setupAutoLayout() {
// Available from iOS 9 commonly known as Anchoring System for AutoLayout...
redView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
redView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -20).isActive = true
redView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
redView.heightAnchor.constraint(equalToConstant: 300).isActive = true
// You can also modified above last two lines as follows by commenting above & uncommenting below lines...
// redView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
// redView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
}
Type of Constraints
/*
// regular use
1.leftAnchor
2.rightAnchor
3.topAnchor
// intermediate use
4.widthAnchor
5.heightAnchor
6.bottomAnchor
7.centerXAnchor
8.centerYAnchor
// rare use
9.leadingAnchor
10.trailingAnchor
etc. (note: very project to project)
*/
Auto layout is realized by applying constraints on images. Use NSLayoutConstraint. It is possible to implement an ideal and beautiful design on all devices. Please try the code below.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let myImageView:UIImageView = UIImageView()
myImageView.backgroundColor = UIColor.red
myImageView.image = UIImage(named:"sample_dog")!
myImageView.translatesAutoresizingMaskIntoConstraints = false
myImageView.layer.borderColor = UIColor.red.cgColor
myImageView.layer.borderWidth = 10
self.view.addSubview(myImageView)
view.removeConstraints(view.constraints)
view.addConstraint(NSLayoutConstraint(
item: myImageView,
attribute: .top,
relatedBy: .equal,
toItem: view,
attribute: .top,
multiplier: 1,
constant:100)
)
view.addConstraint(NSLayoutConstraint(
item: myImageView,
attribute: .centerX,
relatedBy: .equal,
toItem: view,
attribute: .centerX,
multiplier: 1,
constant:0)
)
view.addConstraint(NSLayoutConstraint(
item: myImageView,
attribute: .height,
relatedBy: .equal,
toItem: view,
attribute: .width,
multiplier: 0.5,
constant:40))
view.addConstraint(NSLayoutConstraint(
item: myImageView,
attribute: .width,
relatedBy: .equal,
toItem: view,
attribute: .width,
multiplier: 0.5,
constant:40))
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
the following code works for me in this scenario: an UIImageView forced landscape.
imagePreview!.isUserInteractionEnabled = true
imagePreview!.isExclusiveTouch = true
imagePreview!.contentMode = UIView.ContentMode.scaleAspectFit
// Remove all constraints
imagePreview!.removeAllConstraints()
// Add the new constraints
let guide = view.safeAreaLayoutGuide
imagePreview!.translatesAutoresizingMaskIntoConstraints = false
imagePreview!.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
imagePreview!.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
imagePreview!.heightAnchor.constraint(equalTo: guide.heightAnchor, multiplier: 1.0).isActive = true
where removeAllConstraints is an extension
extension UIView {
func removeAllConstraints() {
var _superview = self.superview
func removeAllConstraintsFromView(view: UIView) { for c in view.constraints { view.removeConstraint(c) } }
while let superview = _superview {
for constraint in superview.constraints {
if let first = constraint.firstItem as? UIView, first == self {
superview.removeConstraint(constraint)
}
if let second = constraint.secondItem as? UIView, second == self {
superview.removeConstraint(constraint)
}
}
_superview = superview.superview
}
self.removeConstraints(self.constraints)
self.translatesAutoresizingMaskIntoConstraints = true
}
}
If you find the above to be ugly. You should consider using a DSL for constraints. Such as SnapKit
Makes constraint API much more user-friendly
view.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
Would like to add some theoretical concept to Imanou Petit’s answer, so that one can understand how auto layout works.
To understand auto layout consider your view as rubber's object which is shrinked initially.
To place an object on screen we need 4 mandatory things :
X coordinate of object (horizontal position).
Y coordinate of object (vertical position )
Object’s Width
Object’s Height.
1 X coordinate: There are multiple ways of giving x coordinates to a view.
Such as Leading constraint, Trailing constraint , Horizontally centre
etc.
2 Y coordinate: There are multiple ways of giving y coordinates to a view :
Such as Top constraint, Bottom constraint , Vertical centre etc.
3 Object's width: There are two ways of giving width constrain to a view :
a. Add fixed width constraint (consider this constraint as iron rod of fixed width and you have hooked your rubber’s object horizontally with it so rubber’s object don’t shrink or expand)
b. Do not add any width constraint but add x coordinate constraint to both end of view trailing and leading, these two constraints will expand/shrink your rubber’s object by pulling/pushing it from both end, leading and trailing.
4 Object's height: Similar to width, there are two ways of giving height constraint to a view as well :
a. Add fixed height constraint (consider this constraints as iron rod of fixed height and you have hooked your rubber’s object vertically with it so rubber’s object don’t shrink or expand)
b. Do not add any height constraint but add x coordinate constraint to both end of view top and bottom, these two constraints will expand/shrink your rubber’s object pulling/pushing it from both end, top and bottom.
it is a little different in xcode 7.3.1. this is what i come up with
// creating the view
let newView = UIView()
newView.backgroundColor = UIColor.redColor()
newView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(newView)
// creating the constraint
// attribute and relation cannot be set directyl you need to create a cariable of them
let layout11 = NSLayoutAttribute.CenterX
let layout21 = NSLayoutRelation.Equal
let layout31 = NSLayoutAttribute.CenterY
let layout41 = NSLayoutAttribute.Width
let layout51 = NSLayoutAttribute.Height
let layout61 = NSLayoutAttribute.NotAnAttribute
// defining all the constraint
let horizontalConstraint = NSLayoutConstraint(item: newView, attribute: layout11, relatedBy: layout21, toItem: view, attribute: layout11, multiplier: 1, constant: 0)
let verticalConstraint = NSLayoutConstraint(item: newView, attribute: layout31, relatedBy: layout21, toItem: view, attribute: layout31, multiplier: 1, constant: 0)
let widthConstraint = NSLayoutConstraint(item: newView, attribute: layout41, relatedBy: layout21, toItem: nil, attribute: layout61, multiplier: 1, constant: 100)
let heightConstraint = NSLayoutConstraint(item: newView, attribute: layout51, relatedBy: layout21, toItem: nil, attribute: layout61, multiplier: 1, constant: 100)
// adding all the constraint
NSLayoutConstraint.activateConstraints([horizontalConstraint,verticalConstraint,widthConstraint,heightConstraint])
This is one way to adding constraints programmatically
override func viewDidLoad() {
super.viewDidLoad()
let myLabel = UILabel()
myLabel.labelFrameUpdate(label: myLabel, text: "Welcome User", font: UIFont(name: "times new roman", size: 40)!, textColor: UIColor.red, textAlignment: .center, numberOfLines: 0, borderWidth: 2.0, BorderColor: UIColor.red.cgColor)
self.view.addSubview(myLabel)
let myLabelhorizontalConstraint = NSLayoutConstraint(item: myLabel, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0)
let myLabelverticalConstraint = NSLayoutConstraint(item: myLabel, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0)
let mylabelLeading = NSLayoutConstraint(item: myLabel, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.leading, multiplier: 1, constant: 10)
let mylabelTrailing = NSLayoutConstraint(item: myLabel, attribute: NSLayoutAttribute.trailing, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.trailing, multiplier: 1, constant: -10)
let myLabelheightConstraint = NSLayoutConstraint(item: myLabel, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 50)
NSLayoutConstraint.activate(\[myLabelhorizontalConstraint, myLabelverticalConstraint, myLabelheightConstraint,mylabelLeading,mylabelTrailing\])
}
extension UILabel
{
func labelFrameUpdate(label:UILabel,text:String = "This is sample Label",font:UIFont = UIFont(name: "times new roman", size: 20)!,textColor:UIColor = UIColor.red,textAlignment:NSTextAlignment = .center,numberOfLines:Int = 0,borderWidth:CGFloat = 2.0,BorderColor:CGColor = UIColor.red.cgColor){
label.translatesAutoresizingMaskIntoConstraints = false
label.text = text
label.font = font
label.textColor = textColor
label.textAlignment = textAlignment
label.numberOfLines = numberOfLines
label.layer.borderWidth = borderWidth
label.layer.borderColor = UIColor.red.cgColor
}
}
var xCenterConstraint : NSLayoutConstraint!
var yCenterConstraint: NSLayoutConstraint!
xCenterConstraint = NSLayoutConstraint(item: self.view, attribute: .CenterX, relatedBy: .Equal, toItem: (Your view NAme), attribute: .CenterX, multiplier: 1, constant: 0)
self.view.addConstraint(xCenterConstraint)
yCenterConstraint = NSLayoutConstraint(item: self.view, attribute: .CenterY, relatedBy: .Equal, toItem: (Your view Name), attribute: .CenterY, multiplier: 1, constant: 0)
self.view.addConstraint(yCenterConstraint)
Try this elegant UIView extension for constraints. You can do constraints easy as:
- firstView.coverWholeSuperview()
- firstView.constraints(size: CGSize(width: 44, height: 44), centerX: view.centerXAnchor, centerY: view.centerXAnchor)
- firstView.constraints(top: view.topAnchor,
leading: secondView.leadingAnchor,
bottom: view.bottomAnchor,
trailing: secondView.trailingAnchor,
padding: UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12))
Here is extension, just copy it to your project.
extension UIView {
/// Attaches all sides of the receiver to its parent view
func coverWholeSuperview(margin: CGFloat = 0.0) {
let view = superview
layoutAttachTop(to: view, margin: margin)
layoutAttachBottom(to: view, margin: margin)
layoutAttachLeading(to: view, margin: margin)
layoutAttachTrailing(to: view, margin: margin)
}
/// Attaches the top of the current view to the given view's top if it's a superview of the current view
/// or to it's bottom if it's not (assuming this is then a sibling view).
#discardableResult
func layoutAttachTop(to: UIView? = nil, margin: CGFloat = 0.0) -> NSLayoutConstraint {
let view: UIView? = to ?? superview
let isSuperview = view == superview
let constraint = NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal,
toItem: view, attribute: isSuperview ? .top : .bottom, multiplier: 1.0,
constant: margin)
superview?.addConstraint(constraint)
return constraint
}
/// Attaches the bottom of the current view to the given view
#discardableResult
func layoutAttachBottom(to: UIView? = nil, margin: CGFloat = 0.0, priority: UILayoutPriority? = nil) -> NSLayoutConstraint {
let view: UIView? = to ?? superview
let isSuperview = (view == superview) || false
let constraint = NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal,
toItem: view, attribute: isSuperview ? .bottom : .top, multiplier: 1.0,
constant: -margin)
if let priority = priority {
constraint.priority = priority
}
superview?.addConstraint(constraint)
return constraint
}
/// Attaches the leading edge of the current view to the given view
#discardableResult
func layoutAttachLeading(to: UIView? = nil, margin: CGFloat = 0.0) -> NSLayoutConstraint {
let view: UIView? = to ?? superview
let isSuperview = (view == superview) || false
let constraint = NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal,
toItem: view, attribute: isSuperview ? .leading : .trailing, multiplier: 1.0,
constant: margin)
superview?.addConstraint(constraint)
return constraint
}
/// Attaches the trailing edge of the current view to the given view
#discardableResult
func layoutAttachTrailing(to: UIView? = nil, margin: CGFloat = 0.0, priority: UILayoutPriority? = nil) -> NSLayoutConstraint {
let view: UIView? = to ?? superview
let isSuperview = (view == superview) || false
let constraint = NSLayoutConstraint(item: self, attribute: .trailing, relatedBy: .equal,
toItem: view, attribute: isSuperview ? .trailing : .leading, multiplier: 1.0,
constant: -margin)
if let priority = priority {
constraint.priority = priority
}
superview?.addConstraint(constraint)
return constraint
}
// For anchoring View
struct AnchoredConstraints {
var top, leading, bottom, trailing, width, height, centerX, centerY: NSLayoutConstraint?
}
#discardableResult
func constraints(top: NSLayoutYAxisAnchor? = nil, leading: NSLayoutXAxisAnchor? = nil, bottom: NSLayoutYAxisAnchor? = nil,
trailing: NSLayoutXAxisAnchor? = nil, padding: UIEdgeInsets = .zero, size: CGSize = .zero,
centerX: NSLayoutXAxisAnchor? = nil, centerY: NSLayoutYAxisAnchor? = nil,
centerXOffset: CGFloat = 0, centerYOffset: CGFloat = 0) -> AnchoredConstraints {
translatesAutoresizingMaskIntoConstraints = false
var anchoredConstraints = AnchoredConstraints()
if let top = top {
anchoredConstraints.top = topAnchor.constraint(equalTo: top, constant: padding.top)
}
if let leading = leading {
anchoredConstraints.leading = leadingAnchor.constraint(equalTo: leading, constant: padding.left)
}
if let bottom = bottom {
anchoredConstraints.bottom = bottomAnchor.constraint(equalTo: bottom, constant: -padding.bottom)
}
if let trailing = trailing {
anchoredConstraints.trailing = trailingAnchor.constraint(equalTo: trailing, constant: -padding.right)
}
if size.width != 0 {
anchoredConstraints.width = widthAnchor.constraint(equalToConstant: size.width)
}
if size.height != 0 {
anchoredConstraints.height = heightAnchor.constraint(equalToConstant: size.height)
}
if let centerX = centerX {
anchoredConstraints.centerX = centerXAnchor.constraint(equalTo: centerX, constant: centerXOffset)
}
if let centerY = centerY {
anchoredConstraints.centerY = centerYAnchor.constraint(equalTo: centerY, constant: centerYOffset)
}
[anchoredConstraints.top, anchoredConstraints.leading, anchoredConstraints.bottom,
anchoredConstraints.trailing, anchoredConstraints.width,
anchoredConstraints.height, anchoredConstraints.centerX,
anchoredConstraints.centerY].forEach { $0?.isActive = true }
return anchoredConstraints
}
}
You are adding all defined constraints to self.view which is wrong, as width and height constraint should be added to your newView.
Also, as I understand you want to set constant width and height 100:100.
In this case you should change your code to:
var constW = NSLayoutConstraint(item: newView,
attribute: .Width,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1,
constant: 100)
newView.addConstraint(constW)
var constH = NSLayoutConstraint(item: newView,
attribute: .Height,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1,
constant: 100)
newView.addConstraint(constH)
You can use Snapkit to set constraints programmatically.
class ViewController: UIViewController {
let rectView: UIView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
private func setupViews() {
rectView.backgroundColor = .red
view.addSubview(rectView)
rectView.snp.makeConstraints {
$0.center.equalToSuperview()
}
}
}
The error is caused by constrains automatically created from autoresizing mask, they are created because UIView property translatesAutoresizingMaskIntoConstraints is true by default.
Consider using BoxView to get rid of all manual constraint creation boilerplate, and make your code concize and readable. To make layout in question with BoxView is very easy:
boxView.items = [
new_view.boxed.centerX().centerY().relativeWidth(1.0).relativeHeight(1.0)
]

Syntax for Swift 3 AddConstraintWithFormat?

I found sourcode:
view.addConstraintsWithFormat("H:|[v0]|", views: menuBar)
view.addConstraintsWithFormat("V:|[v0(50)]", views: menuBar)
And I'm using Swift 3, it's not working in my Xcode 8
Can someone tell me what code for that ?
Thank you so much!
You should write a function like this:
func addContraintsWithFormat(_ format: String, views: UIView...) {
var viewDict = [String: UIView]()
for (index, view) in views.enumerated() {
let key = "v\(index)"
view.translatesAutoresizingMaskIntoConstraints = false
viewDict[key] = view
}
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: viewDict))
}
It would work for you!
May be you need Visual Format Language to use this function.
You have to write extension for UIView. Something like that:
extension UIView {
func addConstraintsWithFormat(_ format: String, views: UIView...) {
var viewsDictionary = [String: UIView]()
for (index, view) in views.enumerated() {
let key = "v\(index)"
view.translatesAutoresizingMaskIntoConstraints = false
viewsDictionary[key] = view
}
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
}
}
Swift 4 Implementation of the same constraints, just in case someone wants to use the new format instead of extending UIView:
private func setupConstraints(){
//topView is a simple UIView with a red background for example purposes
view.addSubview(topView)
topView.translatesAutoresizingMaskIntoConstraints = false
let cn1 = NSLayoutConstraint(item: topView, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1.0, constant: 0)
//Menu bar trailing end is 20 px from right edge of the screen
let cn2 = NSLayoutConstraint(item: topView, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailing, multiplier: 1.0, constant: 0)
//Menu bar height = constant 60
let cn3 = NSLayoutConstraint(item: topView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 50)
//Menu bar width greater than or equal to 400
let cn4 = NSLayoutConstraint(item: topView, attribute: .width, relatedBy: .greaterThanOrEqual, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: 400)
//Menu bar vertical padding from the top edge of the screen = 20
let cn5 = NSLayoutConstraint(item: topView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1.0, constant: 50)
view.addConstraint(cn1)
view.addConstraint(cn2)
view.addConstraint(cn3)
view.addConstraint(cn4)
view.addConstraint(cn5)
}
You can use below code
let tempView = UIView()
tempView.backgroundColor = UIColor.green
tempView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tempView)
let views = ["view": view, "tempView": tempView]
let horizontalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "H:[view]-(<=0)-[newView(100)]", options: NSLayoutFormatOptions.alignAllCenterY, metrics: nil, views: views)
let verticalConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:[view]-(<=0)-[tempView(100)]", options: NSLayoutFormatOptions.alignAllCenterX, metrics: nil, views: views)
view.addConstraints(horizontalConstraints)
view.addConstraints(verticalConstraints)
Let me know if this help you or not.

how can I center two elements (without knowing their width) next to each other in Swift?

In my swift app I have a view with a UILabel and a UIButton. In the storyboard it looks like this:
I know I can group those two elements and then put constraints on that group, but that will only work when the UILabel has constant width.
I want to display this group like this:
| label X |
or - when the label is longer, like this:
| longerlabel X |
How should I apply constraints to get that effect?
At first I considered UILayoutGuides, but it's much easier than that, as long as you are willing to code a few things. Use UILayoutAnchors, centerX, and a multiplier:
myLabel.centerXAnchor.constraint(equalTo: myView.centerXAnchor, multiplier: 0.33)
myButton.centerXAnchor.constraint(equalTo: myView.centerXAnchor, multiplier: 0.667)
Of course, you need to layout more than this (vertical position in particular) and yes, you can use UILayoutGuides for equal spacing. But as long as you are using auto layout and understand how to set up IBOutlets for the things you need to code for that way, this will work.
BTW, you can be exact, as in reference the superview in code to center it perfectly.)
you should use a regular UIView as a container. you could set your views up in code like this:
// configure the content
let labelText = "Label"
let buttonTitle = "X"
// setup the views
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = labelText
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle(buttonTitle, for: .normal)
button.setContentCompressionResistancePriority(label.contentCompressionResistancePriority(for: .horizontal) + 1, for: .horizontal)
let container = UIView()
container.translatesAutoresizingMaskIntoConstraints = false
container.layer.borderColor = UIColor.lightGray.cgColor
container.layer.borderWidth = 1
// add the views
container.addSubview(label)
container.addSubview(button)
view.addSubview(container)
// create the container constraints
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "|[lbl]-[btn]|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: ["lbl": label, "btn": button]))
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|[btn]|", options: [], metrics: nil, views: ["btn": button]))
// center the container
NSLayoutConstraint(item: container, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: container, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0).isActive = true
// make sure the container does not extend the view's width
NSLayoutConstraint(item: container, attribute: .leading, relatedBy: .greaterThanOrEqual, toItem: view, attribute: .leading, multiplier: 1, constant: 20).isActive = true
feel free to ask if anything is unclear! this is the result btw:

Hot to add a View to a Scrollview programmatically using layout constraints

I'm triying to instert programatically a view inside an UIScrollView, but it doesn't appear here is my code:
mainScrollView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(mainScrollView)
//Add Trailing
let trailingConstraint = NSLayoutConstraint(item: mainScrollView, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailing, multiplier: 1, constant: 0)
self.view.addConstraint(trailingConstraint)
//Add Leading
let leadingConstraint = NSLayoutConstraint(item: mainScrollView, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1, constant: 0)
self.view.addConstraint(leadingConstraint)
//Add Top
let topConstraint = NSLayoutConstraint(item: mainScrollView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 0)
self.view.addConstraint(topConstraint)
//Add Bottom
let bottomConstraint = NSLayoutConstraint(item: mainScrollView, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: 0)
self.view.addConstraint(bottomConstraint)
let contentView = UIView()
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.backgroundColor = .blue
mainScrollView.addSubview(contentView)
//Add Trailing
let trailingConstraintContent = NSLayoutConstraint(item: contentView, attribute: .trailing, relatedBy: .equal, toItem: mainScrollView, attribute: .trailing, multiplier: 1, constant: 0)
mainScrollView.addConstraint(trailingConstraintContent)
//Add Leading
let leadingConstraintContent = NSLayoutConstraint(item: contentView, attribute: .leading, relatedBy: .equal, toItem: mainScrollView, attribute: .leading, multiplier: 1, constant: 0)
mainScrollView.addConstraint(leadingConstraintContent)
//Add Top
let topConstraintContent = NSLayoutConstraint(item: contentView, attribute: .top, relatedBy: .equal, toItem: mainScrollView, attribute: .top, multiplier: 1, constant: 0)
mainScrollView.addConstraint(topConstraintContent)
//Add Bottom
let bottomConstraintContent = NSLayoutConstraint(item: contentView, attribute: .bottom, relatedBy: .equal, toItem: mainScrollView, attribute: .bottom, multiplier: 1, constant: 0)
mainScrollView.addConstraint(bottomConstraintContent)
The first ScrollView is inserted because I added a background color and I can see it, but I cant see the contentview added as background color blue.
Any help?
UPDATE
I have tried the following to and no success:
mainScrollView.translatesAutoresizingMaskIntoConstraints = false
let contentView = UIView()
contentView.translatesAutoresizingMaskIntoConstraints = false
mainScrollView.backgroundColor = .red
contentView.backgroundColor = .blue
self.view.addSubview(mainScrollView)
mainScrollView.addSubview(contentView)
let viewsDictionary = ["mainScrollView": mainScrollView, "contentView": contentView]
let mainScrollViewVerticalConstraint = NSLayoutConstraint.constraints(withVisualFormat: "V:|[mainScrollView]|", options: [], metrics: nil, views: viewsDictionary)
let mainScrollViewHorizontalConstraint = NSLayoutConstraint.constraints(withVisualFormat: "H:|[mainScrollView]|", options: [], metrics: nil, views: viewsDictionary)
self.view.addConstraints(mainScrollViewVerticalConstraint)
self.view.addConstraints(mainScrollViewHorizontalConstraint)
let contentViewVerticalConstraint = NSLayoutConstraint.constraints(withVisualFormat: "V:|[contentView]|", options: [], metrics: nil, views: viewsDictionary)
let contentViewHorizontalConstraint = NSLayoutConstraint.constraints(withVisualFormat: "H:|[contentView]|", options: [], metrics: nil, views: viewsDictionary)
mainScrollView.addConstraints(contentViewVerticalConstraint)
mainScrollView.addConstraints(contentViewHorizontalConstraint)
When adding sub views the important thing to remember is to pin them in a way to make it possible for Auto Layout to evaluate the height, this is because the scroll view determines the content size by determining the sum of heights sub views (in case of vertical).
So you should specify the height in VFL . eg.
let contentViewVerticalConstraint=NSLayoutConstraint.constraints(withVisualFormat: "V:|[contentView(300)]", options: [], metrics: nil, views: viewsDictionary)
Create a custom view using .xib file
Apply the constraints for the custom view child components in .xib (auto layout)
Create a custom view instant
MyView *myview = [[[NSBundle mainBundle] loadNibNamed:#"nibName" owner:nil options:nil] objectAtIndex:0];
Set its frame for screen width as
CGRect screenSize = [UIScreen mainScreen].bounds.size;
myView = CGRectMake(0.0, 0.0, screenSize.width, 100.0);
Add to parent view
[parentView addSubView:myView];

Swift constraints programmatically creates odd behavior

I'm attempting to layout all my constraints programmatically because I have to add and remove boxes based on user input. When I run a function which applies all my constraints in ViewDidLoad, it works great. But if I run it again, after removing them all, 2 of my labels disappear behind my navigation bar. I can't figure out why! Keep in mind if I never run the function pasted below, my screen is blank because I have no constraints on start up from the storyboard. My goal is to figure out why my label and button disappear when the same piece of code is run a second time.
Below is a picture when the code, which I'll post below, is run initially in ViewDidLoad:
Below is a picture when the same piece of code is run again afterwards when the next button is pressed. This is just a test, in the future I'll need to reset all my constraints after changing them:
And Below is a picture of the debug view hierarchy afterwards:
The code that creates the constraints is below. Keep in mind I have only 5 elements, my main view, scroll view, aglAltitudeLabel which is the label, nextButton which is the button, and aglAltitude which is the text input:
//Remove all pre-existing contraints
var constraints:NSArray = aglAltitudeLabel.constraints()
aglAltitudeLabel.removeConstraints(constraints)
constraints = aglAltitude.constraints()
aglAltitude.removeConstraints(constraints)
constraints = nextButton.constraints()
nextButton.removeConstraints(constraints)
constraints = scrollView.constraints()
scrollView.removeConstraints(constraints)
constraints = view.constraints()
self.view.removeConstraints(constraints)
constraints = calculateButton.constraints()
calculateButton.removeConstraints(constraints)
//Make scrollview fit normal View, accounting for Navigation bar
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|[first(\(self.view.frame.width))]|", options: nil, metrics: nil, views: ["first": scrollView]))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-64-[first(\(self.view.frame.height-64))]|", options: nil, metrics: nil, views: ["first": scrollView]))
//Set the heights for the label and button
let labelHeight = NSLayoutConstraint(item: aglAltitudeLabel, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 20)
scrollView.addConstraint(labelHeight)
let buttonHeight = NSLayoutConstraint(item: nextButton, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 30)
scrollView.addConstraint(buttonHeight)
//Setup the vertical layout
scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-8-[first(30)]-8-|", options: nil, metrics: nil, views: ["first": aglAltitude]))
//Setup the horizontal layout
scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-8-[first(97)]-8-[second(>=100)]-8-[third(46)]-8-|", options: nil, metrics: nil, views: ["first": aglAltitudeLabel, "second": aglAltitude, "third": nextButton]))
//Align baselines - this also occurs with the option above of AlignAllBaselines and leaving this code out
let aglLabelBaseline = NSLayoutConstraint(item: aglAltitudeLabel, attribute: NSLayoutAttribute.Baseline, relatedBy: NSLayoutRelation.Equal, toItem: aglAltitude, attribute: NSLayoutAttribute.Baseline, multiplier: 1.0, constant: 0)
scrollView.addConstraint(aglLabelBaseline)
let nextButtonBaseline = NSLayoutConstraint(item: nextButton, attribute: NSLayoutAttribute.Baseline, relatedBy: NSLayoutRelation.Equal, toItem: aglAltitude, attribute: NSLayoutAttribute.Baseline, multiplier: 1.0, constant: 0)
scrollView.addConstraint(nextButtonBaseline)
self.view.layoutSubviews()
Thank you for any help. I'm sure I'm doing 55 things incorrectly.
Edit: If I change the last section of code from this:
//Align baselines - this also occurs with the option above of AlignAllBaselines and leaving this code out
let aglLabelBaseline = NSLayoutConstraint(item: aglAltitudeLabel, attribute: NSLayoutAttribute.Baseline, relatedBy: NSLayoutRelation.Equal, toItem: aglAltitude, attribute: NSLayoutAttribute.Baseline, multiplier: 1.0, constant: 0)
scrollView.addConstraint(aglLabelBaseline)
let nextButtonBaseline = NSLayoutConstraint(item: nextButton, attribute: NSLayoutAttribute.Baseline, relatedBy: NSLayoutRelation.Equal, toItem: aglAltitude, attribute: NSLayoutAttribute.Baseline, multiplier: 1.0, constant: 0)
scrollView.addConstraint(nextButtonBaseline)
To this:
//Align baselines - this also occurs with the option above of AlignAllBaselines and leaving this code out
let aglLabelBaseline = NSLayoutConstraint(item: aglAltitudeLabel, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 14)
self.view.addConstraint(aglLabelBaseline)
let nextButtonBaseline = NSLayoutConstraint(item: nextButton, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 8)
self.view.addConstraint(nextButtonBaseline)
it works exactly as I'd expect. Why the difference?
Without knowing what your requirements are, it's hard to give you good advice. Here is an example of how I might do it. I added all the views in code, and added background colors to the views so I could better see their layout. I'm assuming that the scroll view is only added once, and never removed, so it's constraints to self.view should only be added once in viewDidLoad. Same goes for the height of the the other views if you don't need to change them (notice that I add the height constraints to the view itself, not its superview). The only thing that needs to be removed and re-added are the constraints between the scroll view and its subviews.
class ViewController: UIViewController {
var aglAltitudeLabel = UILabel()
var aglAltitude = UITextField()
var nextButton = UIButton.buttonWithType(.System) as UIButton
var scrollView = UIScrollView()
var viewsDict: NSDictionary!
override func viewDidLoad() {
super.viewDidLoad()
aglAltitudeLabel.text = "AGL Altitude"
aglAltitudeLabel.backgroundColor = UIColor.lightGrayColor()
aglAltitudeLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
aglAltitude.setTranslatesAutoresizingMaskIntoConstraints(false)
aglAltitude.backgroundColor = UIColor.yellowColor()
aglAltitudeLabel.addConstraint(NSLayoutConstraint(item: aglAltitudeLabel, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 30))
scrollView.setTranslatesAutoresizingMaskIntoConstraints(false)
nextButton.setTranslatesAutoresizingMaskIntoConstraints(false)
nextButton.setTitle("Next", forState: .Normal)
nextButton.backgroundColor = UIColor.lightGrayColor()
nextButton.addConstraint(NSLayoutConstraint(item: nextButton, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 30))
self.view.addSubview(scrollView)
scrollView.addSubview(aglAltitude)
scrollView.addSubview(aglAltitudeLabel)
scrollView.addSubview(nextButton)
scrollView.backgroundColor = UIColor(red: 1, green: 1, blue: 0.8, alpha: 1)
viewsDict = ["scrollView": scrollView, "aglAltitudeLabel": aglAltitudeLabel, "aglAltitude":aglAltitude, "nextButton":nextButton] as NSDictionary
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|[scrollView]|", options: nil, metrics: nil, views: viewsDict))
self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-64-[scrollView]|", options: nil, metrics: nil, views: viewsDict))
self.redoConstraints()
}
#IBAction func redoConstraints() {
scrollView.removeConstraints(scrollView.constraints())
scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-8-[aglAltitude(30)]-8-|", options: nil, metrics: nil, views: viewsDict))
scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-8-[aglAltitudeLabel(97)]-8-[aglAltitude(>=100)]-8-[nextButton(46)]-8-|", options: .AlignAllBottom , metrics: nil, views: viewsDict))
}
}
Notice that I took the fixed width and height out of the constraints for the scroll view. There's no need for that, and it would cause it to not work correctly on rotation.

Resources