How do I add contstraints automatically - ios

I come from front-end development, when I create div it automatically displays itself in top, after second one it will display itself below the first one, and so...
I want to be able to do the same with swift UIViews, I've created some views and want to add constraints automatically after previous view, and if the view is the first one apply it to the parent view, how do I do it any suggestions? I have created custom class from UIView called Car, but then I realized it was not a good idea
if let parent = self.superview {
let siblings = parent.subviews.filter{$0 is Car}
let lastCar: UIView?
if siblings.count > 1{
lastCar = siblings[siblings.count - 2]
} else {
lastCar = nil
}
if let sibling = lastCar {
parent.addConstraint(
NSLayoutConstraint(
item: self,
attribute: .Top,
relatedBy: .Equal,
toItem: sibling,
attribute: .Bottom,
multiplier: 1.0,
constant: 1
))
} else {
parent.addConstraint(
NSLayoutConstraint(
item: self,
attribute: .Top,
relatedBy: .Equal,
toItem: parent,
attribute: .Top,
multiplier: 1.0,
constant: 0
))
}
}
I set the constraints to lastCar or parent view using if let syntax.

Related

Collapsable Tableview with Dynamic sizing cells

I've been trying to follow this github tutorial, however instead of having static cells, they need to be dynamic since I don't know the height at compile time.
In the tableViewController I have set the heightForRowAt to be 0 or UITableViewAutomaticDimension depending if the section is collapsed or not. Similar with the estimatedHeightForRowAtthe value is 0 or a higher value if it's expanded.
Getting to the problem now, I have subclassed the UITableViewCell and when the cellForRowAt is called it will add a subview like the following:
override func addSubview(_ view: UIView) {
guard view != nil else {return}
for view in self.subviews {
view.removeFromSuperview()
}
super.addSubview(view)
//view.applyLeadingAndTrailingPinConstraint(toSuperview: 0)
//view.applyTopAndBottomPinConstraint(toSuperview: 0)
let addedView = self.subviews[0]
let bottom = NSLayoutConstraint(item: addedView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0)
bottom.priority = 750
bottom.isActive = true
NSLayoutConstraint(item: addedView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: addedView, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: addedView, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: addedView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: addedView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0).isActive = true
//NSLayoutConstraint.reportAmbiguity(v: self)
if addedView.hasAmbiguousLayout {
print("Horizontal: \(addedView.constraintsAffectingLayout(for: .horizontal))")
print("Vertical: \(addedView.constraintsAffectingLayout(for: .vertical))")
print("Trace: \(addedView.value(forKey: "_autolayoutTrace"))")
}
//addedView.exerciseAmbiguityInLayout()
//print("self: \(self.frame), subview: \(self.subviews[0].frame)")
}
As you may see, the problem is that there's an ambiguity with the constraints, hence the hasAmbigousLayout. When the view is collapsed there is no error. However when I expand the view, which has also a height constraint, the ambiguity happens. The following is the output of the 3 print statements inside hasAmbigousLayout:
Horizontal: []
Vertical: [<NSLayoutConstraint:0x60800028ea10 UIView:0x7fcfe856a790.height == 100 (active)>]
Trace: Optional(
•ViraVira_Info.WellnessCell:0x7fcfe8859a00'cell', MISSING HOST CONSTRAINTS
| *UIView:0x7fcfe856a790- AMBIGUOUS LAYOUT for UIView:0x7fcfe856a790.minX{id: 776}, UIView:0x7fcfe856a790.minY{id: 771}, UIView:0x7fcfe856a790.Width{id: 778}
Legend:
* - is laid out with auto layout
+ - is laid out manually, but is represented in the layout engine because translatesAutoresizingMaskIntoConstraints = YES
• - layout engine host)
I have tried to lower the bottom constraint priority as sugested in this question. Also I have tried many other sites, which I sadly lost track of.
The result in the app is that the view does not even show up, since it should have a red background, also when i try to print the frame of the subview, the width is 0 which seems odd.
I probably made a silly mistake with the constraint but after 2 days trying I still can't figure it out.
Thanks for your time
-Jorge
There are several issues to note (and may be part of the problem):
override func addSubview(_ view: UIView) -> I don't think thats the right place for your code. I develop several years now iOS and there was never a reason to override that method. View layout should be done in Interface Builder or in the parent view.
guard view != nil else {return} -> should give you a warning, because view is not an optional (UIView?), so it can't be nil. This check does simply nothing useful.
for view in self.subviews { view.removeFromSuperview() } super.addSubview(view) -> This will remove every previously added view, the last added view will win the race, thats completely unexpected by the caller of addSubview - If a cell adds more than one subview this will cause trouble and headache.
let addedView = self.subviews[0] -> must be equal view, so why not using view in the later code?
6 * NSLayoutConstraint -> A view is normally positioned by 4 constraints, 2 vertical and 2 horizontal - I think you should remove the last two (center constraints) - That may be your problem.
Try to refactor your code...

How to resize UITableView with a large list to fit all the content without scrolling?

I have a UITableView with about at least 60 cells inside that needs to fit and not scroll. How can I get it to fit? I have already tried:
CGRect frame = self.tableView.frame;
frame.size.height = self.tableView.contentSize.height;
self.tableView.frame = frame;
As said in this answer here but in the comments it was mentioned that it would not work with very large lists and you can tell because the bounce will reveal more cells. There was not a solution provided for this. How can I fix this problem?
Obvious solution is to not use UITableView. You can create your contentView as normal view, either programatically or in xib, and then add it to your main view and connect it to the previous custom one using constraint. I have done it multiple times and it works like a charm.
var lastView: UIView? = nil
for item in model.items {
var customView: CustomContentView
let objects = NSBundle.mainBundle().loadNibNamed("CustomContentView", owner: self, options: nil)
customView = objects.first! as! CustomContentView
customView.translatesAutoresizingMaskIntoConstraints = false
customView.setItemModel(item)
viewWrap.addSubview(customView)
viewWrap.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[customView]|", options: [], metrics: nil, views: ["customView": customView]))
if lastView == nil {
viewWrap.addConstraint(NSLayoutConstraint(item: customView, attribute: .Top, relatedBy: .Equal, toItem: viewWrap, attribute: .Top, multiplier: 1, constant: 0))
} else {
viewWrap.addConstraint(NSLayoutConstraint(item: customView, attribute: .Top, relatedBy: .Equal, toItem: lastView!, attribute: .Bottom, multiplier: 1, constant: 0))
}
lastView = customView
}
if lastView != nil {
viewWrap.addConstraint(NSLayoutConstraint(item: viewWrap, attribute: .Bottom, relatedBy: .Equal, toItem: lastView!, attribute: .Bottom, multiplier: 1, constant: 17))
}
Then you just to make sure that your custom view can resize to fit the screen if there is too many of them. I assume you have some max limit and that they all can fit. If not, then you need to add it to scroll view.

Programatic constraints collapsing cell view

I am developing a UITableViewCell that starts as a xib, has views added to it programmatically, and has a dynamically sized height. However, it looks like when adding the programatic views with constraints, it is conflicting with the auto-resize constraint initially applied to the xib, and causing issues. Please see below:
Dequeuing my cells:
//Table Delegate/Datasource
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:S360SSessionMatchTableCell? = tableView.dequeueReusableCellWithIdentifier(XIBFiles.SESSIONMATCHTABLECELL + String(indexPath.row)) as? S360SSessionMatchTableCell
if ((cell == nil)){
tableView.registerNib(UINib(nibName: XIBFiles.SESSIONMATCHTABLECELL, bundle: nil), forCellReuseIdentifier: XIBFiles.SESSIONMATCHTABLECELL + String(indexPath.row))
cell = tableView.dequeueReusableCellWithIdentifier(XIBFiles.SESSIONMATCHTABLECELL + String(indexPath.row)) as? S360SSessionMatchTableCell
}
cell!.setupEvents(sessionMatches[indexPath.row]["sessions"]! as! [[String:String]])
return cell!
}
Setup Events Method in Custom UITableViewCell:
func setupEvents(events:[[String:String]]){
//Set up start and end times
self.startTimeLbl.text = events[0]["startTime"]!
self.endTimeLbl.text = events[events.count - 1]["endTime"]!
//Set up events
var pastEventView:S360SScheduledEventView? = nil
var pastEvent:[String:String]? = nil
for (index, event) in events.enumerate(){
var topAnchor:NSLayoutConstraint!
//Create event view
let eventView:S360SScheduledEventView = NSBundle.mainBundle().loadNibNamed(XIBFiles.SCHEDULEDEVENTVIEW, owner: self, options: nil)[0] as! S360SScheduledEventView
//Deal with first view added
if pastEvent == nil{
//Top anchor setup for first view
topAnchor = NSLayoutConstraint(item: eventView, attribute: .Top, relatedBy: .Equal, toItem: toLbl, attribute: .Bottom, multiplier: 1, constant: 10)
}
else{
//Check for a break
let timeFormatter:NSDateFormatter = NSDateFormatter()
timeFormatter.dateFormat = "hh:mm a"
let startTime = timeFormatter.dateFromString(pastEvent!["endTime"]!)
let endTime = timeFormatter.dateFromString(event["startTime"]!)
if startTime != endTime {
//Create break view
let breakView = NSBundle.mainBundle().loadNibNamed(XIBFiles.SCHEDULEDBREAKVIEW, owner: self, options: nil)[0] as! S360SScheduledBreakView
//Setup breakview constraints
let bTopAnchor = NSLayoutConstraint(item: breakView, attribute: .Top, relatedBy: .Equal, toItem: pastEventView, attribute: .Bottom, multiplier: 1, constant: 0)
let bLeftAnchor = NSLayoutConstraint(item: breakView, attribute: .Leading, relatedBy: .Equal, toItem: self.contentView, attribute: .LeadingMargin, multiplier: 1, constant: 0)
let bRightAnchor = NSLayoutConstraint(item: breakView, attribute: .Trailing, relatedBy: .Equal, toItem: self.contentView, attribute: .TrailingMargin, multiplier: 1, constant: 0)
let bHeightAnchor = NSLayoutConstraint(item: breakView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 30)
//Add break view and constraints
self.addSubview(breakView)
self.addConstraints([bTopAnchor, bLeftAnchor, bRightAnchor, bHeightAnchor])
//Top anchor setup for subsequent view
topAnchor = NSLayoutConstraint(item: eventView, attribute: .Top, relatedBy: .Equal, toItem: breakView, attribute: .Bottom, multiplier: 1, constant: 0)
}
else{
//Top anchor setup for subsequent views
topAnchor = NSLayoutConstraint(item: eventView, attribute: .Top, relatedBy: .Equal, toItem: pastEventView, attribute: .Bottom, multiplier: 1, constant: 0)
}
}
//Setup other anchors
let leftAnchor = NSLayoutConstraint(item: eventView, attribute: .Leading, relatedBy: .Equal, toItem: self.contentView, attribute: .LeadingMargin, multiplier: 1, constant: 0)
let rightAnchor = NSLayoutConstraint(item: eventView, attribute: .Trailing, relatedBy: .Equal, toItem: self.contentView, attribute: .TrailingMargin, multiplier: 1, constant: 0)
let heightAnchor = NSLayoutConstraint(item: eventView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 60)
//Setup event view
eventView.iconImg.image = Images.get_event_image(event["title"]!)
eventView.titleLbl.text = event["title"]!
eventView.courtLbl.text = "court" + event["court"]!
eventView.timeLbl.text = event["startTime"]! + " to " + event["endTime"]!
//Add event view and constraints
self.addSubview(eventView)
self.addConstraints([topAnchor, leftAnchor, rightAnchor, heightAnchor])
//Prepare for next iteration
pastEventView = eventView
pastEvent = event
//Set up last cell with bottom bound
if index == events.count - 1 {
let bottomAnchor = NSLayoutConstraint(item: eventView, attribute: .Bottom, relatedBy: .Equal, toItem: self.contentView, attribute: .BottomMargin, multiplier: 1, constant: 0)
self.addConstraint(bottomAnchor)
}
}
}
Constraints in xib:
This is the error I get (pasted once, but it occurs for each cell):
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.
2016-07-05 15:13:01.654 Shoot360 Scheduler[32779:642808] 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)
(
"<NSAutoresizingMaskLayoutConstraint:0x7fedd85d5590 h=--& v=--& V:[UITableViewCellContentView:0x7fedda431120(44)]>",
"<NSLayoutConstraint:0x7fedda43a7e0 V:[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda438b20(60)]>",
"<NSLayoutConstraint:0x7fedda436590 UITableViewCellContentView:0x7fedda431120.topMargin == UILabel:0x7fedda4312a0'10:00 AM'.top - 15>",
"<NSLayoutConstraint:0x7fedda436630 UILabel:0x7fedda431c00'to'.top == UILabel:0x7fedda4312a0'10:00 AM'.top>",
"<NSLayoutConstraint:0x7fedda433b60 V:[UILabel:0x7fedda431c00'to']-(10)-[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda438b20]>",
"<NSLayoutConstraint:0x7fedda445910 V:[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda4443f0(60)]>",
"<NSLayoutConstraint:0x7fedda448310 V:[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda438b20]-(0)-[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda4443f0]>",
"<NSLayoutConstraint:0x7fedda449a00 V:[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda448540(60)]>",
"<NSLayoutConstraint:0x7fedda4479e0 V:[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda4443f0]-(0)-[Shoot360_Scheduler.S360SScheduledEventView:0x7fedda448540]>",
"<NSLayoutConstraint:0x7fedda44a100 Shoot360_Scheduler.S360SScheduledEventView:0x7fedda448540.bottom == UITableViewCellContentView:0x7fedda431120.bottomMargin>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7fedda436590 UITableViewCellContentView:0x7fedda431120.topMargin == UILabel:0x7fedda4312a0'10:00 AM'.top - 15>
Row height is being set to dynamic:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
//Styling
showAllBtn.layer.cornerRadius = Numbers.CORNERRADIUS
sessionsTbl.rowHeight = UITableViewAutomaticDimension
sessionsTbl.estimatedRowHeight = 500
sessionsTbl.layer.borderColor = Colors.REALLIGHTGREY.CGColor
sessionsTbl.layer.borderWidth = Numbers.BORDERREG
sessionsTbl.layer.cornerRadius = Numbers.CORNERRADIUS
sessionsTbl.separatorStyle = UITableViewCellSeparatorStyle.None
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
The constraint
V:[UITableViewCellContentView:0x7fedda431120(44)
means that rowHeight in your table is set to the default value of 44pt while you want the cell height to be dynamic. You will have to set rowHeight to UITableViewAutomaticDimension and also set estimatedRowHeight.
Also note that cells are reused therefore you will have to remove all previously added views everytime you call setupEvents.
Also note you should not call tableView.registerNib(...) from inside cellForRow method. The good place to register cells is inside viewDidLoad.
It seems you've made life much more complicated for yourself than it needs to be.
If we look at what you currently have:
a table with 1 section and many rows
1 cell subclass
each row has an arbitrary number of subviews added on the fly
each subview is pinned to each other with constraints
each cell is explicitly instantiated for each row, not properly reused
cells don't have their subviews removed when they are reused
this doesn't fit well with a table view and means you're writing a lot of code and trying to cram it all into one place.
Looking at your data it would be better to have something like:
a table with multiple sections, each section having multiple rows
1 section per session
1 row per 'event'
1 section header class with date labels
2 cell subclasses, 1 for an event and one for a break
no views added on the fly
In this scenario your constraints are trivial and there are no constraints being added in code, you just set a bit of data and everything else just works. This scheme also breaks down your concerns and separates out the code for each different part into logical parts.
Rather than try to fix your existing issue you should step back and look at your approach.

Is iOS UILabel constraint from code with margin broken?

I'm trying to implement a popup layout like following:
This works fine in a storyboard with margins and everything. In storyboard it looks like this:
But if I make the same constraint in code I get this result:
The label has a light blue background and the view the label is inside has the dark blue background. The popup background has a border around itself. So basically the popup matches the child but the label inside the child overflows parent and grand parent BUT only because it has margins... If I remove margins it goes right to the border!
I've tryed making the exact same constraint just in code. I'm very open for alternative suggestions involving automatic adjusting width.
My code for creating popup:
func showPopup(caller: UIView) {
closePopups()
// setup view
currentPopup = UIView()
self.view.addSubview(currentPopup)
currentPopup.backgroundColor = UIColorFromHex(Constants.Colors.white, alpha: 1)
// setup constraints
currentPopup.translatesAutoresizingMaskIntoConstraints = false
// top constraint
let topSideConstraint = NSLayoutConstraint(item: currentPopup, attribute: .Top, relatedBy: .Equal, toItem: intoWordsBar.view, attribute: .Bottom, multiplier: 1.0, constant: 0)
self.view.addConstraint(topSideConstraint)
// setup child elements
var children = [PopupChildButton]()
let childOne = createChild("writing_strategy_1", parent: currentPopup, aboveChild: nil, hasBorder: true, feature: FeatureManager.BarFeature.WriteReadLetterName)
children.append(childOne)
let childTwo = createChild("writing_strategy_2", parent: currentPopup, aboveChild: children[0], hasBorder: true, feature: FeatureManager.BarFeature.WriteReadLetterSound)
children.append(childTwo)
let childThree = createChild("writing_strategy_3", parent: currentPopup, aboveChild: children[1], hasBorder: true, feature: FeatureManager.BarFeature.WriteReadWord)
children.append(childThree)
let childFour = createChild("writing_strategy_4", parent: currentPopup, aboveChild: children[2], hasBorder: false, feature: FeatureManager.BarFeature.WriteReadSentence)
children.append(childFour)
let parentSize = getWidth(caller)
//TODO MARK: <-- here working, need to add toggle function and graphics to childrens, documentation on methods, move to constructor class?
// setup rest of constraints
// add bottom constraint, equal to bottom of last child
let bottomSideConstraint = NSLayoutConstraint(item: currentPopup, attribute: .Bottom, relatedBy: .Equal, toItem: children[children.count-1], attribute: .Bottom, multiplier: 1.0, constant: 0)
self.view.addConstraint(bottomSideConstraint)
// left constraint
let leftSideConstraint = NSLayoutConstraint(item: currentPopup, attribute: .Left, relatedBy: .Equal, toItem: caller, attribute: .Right, multiplier: 1.0, constant: (-parentSize)/2)
self.view.addConstraint(leftSideConstraint)
// add border
currentPopup.addBorder(edges: [.All], colour: UIColorFromHex(Constants.Colors.dark_grey, alpha: 1), thickness: 1)
//TODO <-- last piece
//childOne.addTarget(self, action: #selector(KeyboardViewController.childClick(_:)), forControlEvents: .TouchUpInside)
//childTwo.addTarget(self, action: #selector(KeyboardViewController.childClick(_:)), forControlEvents: .TouchUpInside)
//childThree.addTarget(self, action: #selector(KeyboardViewController.childClick(_:)), forControlEvents: .TouchUpInside)
//childFour.addTarget(self, action: #selector(KeyboardViewController.childClick(_:)), forControlEvents: .TouchUpInside)
self.view.setNeedsLayout()
self.view.layoutIfNeeded()
}
My code for creating child:
func createChild(text: String, parent: UIView, aboveChild: UIView?, hasBorder: Bool, feature: FeatureManager.BarFeature) -> PopupChildButton {
// setup child element
let childBtn = PopupChildButton()
childBtn.setRelatedFeature(feature)
// set the right background color
if intoWordsBar.getFeatureManager().isFeatureActive(feature) {
childBtn.backgroundColor = UIColorFromHex(Constants.Colors.light_blue, alpha: 1)
//childBtn.setImage(UIImage(named: "Checkmark"))
} else {
childBtn.backgroundColor = UIColorAndAlphaFromHex(Constants.Colors.transparent)//TODO Highlight implementation needs to be optimized, icon should be moved all the way to the left... somehow //TODO Add new checkmark icon
//childBtn.setImage(nil)
}
childBtn.translatesAutoresizingMaskIntoConstraints = false
parent.addSubview(childBtn)
// add constraints
// top constraint
if let aboveChild = aboveChild {
let topSideConstraint = NSLayoutConstraint(item: childBtn, attribute: .Top, relatedBy: .Equal, toItem: aboveChild, attribute: .Bottom, multiplier: 1.0, constant: 0)
parent.addConstraint(topSideConstraint)
} else {
let topSideConstraint = NSLayoutConstraint(item: childBtn, attribute: .Top, relatedBy: .Equal, toItem: parent, attribute: .Top, multiplier: 1.0, constant: 0)
parent.addConstraint(topSideConstraint)
}
// height constraint
let heightConstraint = NSLayoutConstraint(item: childBtn, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: CGFloat(Constants.Sizes.popupChildHeight))
parent.addConstraint(heightConstraint)
// left constraint
let leftSideConstraint = NSLayoutConstraint(item: parent, attribute: .Leading, relatedBy: .Equal, toItem: childBtn, attribute: .Leading, multiplier: 1.0, constant: 0)
parent.addConstraint(leftSideConstraint)
// right constraint
let rightSideConstraint = NSLayoutConstraint(item: parent, attribute: .Trailing, relatedBy: .Equal, toItem: childBtn, attribute: .Trailing, multiplier: 1.0, constant: 0)
parent.addConstraint(rightSideConstraint)
// add border
if hasBorder {
childBtn.addBorder(edges: .Bottom, colour: UIColorFromHex(Constants.Colors.dark_grey, alpha: 1), thickness: 1)
}
// create grandchildren
let label = UILabel()
// setup looks
label.textColor = UIColorFromHex(Constants.Colors.black, alpha: 1)
label.textAlignment = .Center
childBtn.backgroundColor = UIColorFromHex(Constants.Colors.dark_blue, alpha: 1)
label.backgroundColor = UIColorFromHex(Constants.Colors.light_blue, alpha: 1)
label.text = text.localized
label.translatesAutoresizingMaskIntoConstraints = false
childBtn.addSubview(label)
// add constraints
// left constraint label
let leftLabelConstraint = NSLayoutConstraint(item: label, attribute: .Left, relatedBy: .Equal, toItem: childBtn, attribute: .Left, multiplier: 1.0, constant: CGFloat(Constants.Sizes.popupMargin))
childBtn.addConstraint(leftLabelConstraint)
// right constraint label
let rightLabelConstraint = NSLayoutConstraint(item: label, attribute: .Right, relatedBy: .Equal, toItem: childBtn, attribute: .Right, multiplier: 1.0, constant: CGFloat(Constants.Sizes.popupMargin))
childBtn.addConstraint(rightLabelConstraint)
// top constraint
let labelTopSideConstraint = NSLayoutConstraint(item: label, attribute: .Top, relatedBy: .Equal, toItem: childBtn, attribute: .Top, multiplier: 1.0, constant: 0)
childBtn.addConstraint(labelTopSideConstraint)
// bottom constraint
//let labelBottomSideConstraint = NSLayoutConstraint(item: label, attribute: .Bottom, relatedBy: .Equal, toItem: childBtn, attribute: .Bottom, multiplier: 1.0, constant: 0)
//childBtn.addConstraint(labelBottomSideConstraint)
return childBtn
}
No, it is not broken.
When defining trailing constraints you must set the parent view as the first item and the child view as the second item. This is in reversed order compared to a leading constraint.
I pulled to constraints from a storyboard to illustrate this. These constraints make sure the header has a 10px margin from leading and trailing of parent view.

Adding auto layout constraints between different view controller's view

As an academic exercise for a future UI, I am trying to add constraints between two table views, belonging to two different child view controllers of the root controller. In my RootViewController class below, tvc is displayed as expected in a 400x500 frame, but tvc2 is consuming the entire frame instead of being a 400x500 frame to the right of tvc. Basically, the constraints are apparently being ignored. I'm using an iPad sim in landscape.
override func viewDidLoad() {
super.viewDidLoad()
let v = self.view
v.backgroundColor = UIColor.greenColor()
var tvc :OrderTableViewController = OrderTableViewController(style: UITableViewStyle.Plain)
var tvc2 :OrderTableViewController = OrderTableViewController(style: UITableViewStyle.Plain)
self.addChildViewController(tvc)
self.addChildViewController(tvc2)
v.addSubview(tvc.view)
v.addSubview(tvc2.view)
tvc.didMoveToParentViewController(self)
tvc2.didMoveToParentViewController(self)
//tvc.view.setTranslatesAutoresizingMaskIntoConstraints(false)
//tvc2.view.setTranslatesAutoresizingMaskIntoConstraints(false)
//self.view.setTranslatesAutoresizingMaskIntoConstraints(false)
tvc.view.frame = CGRectMake(0, 0, 400, 500)
self.view.addConstraint(NSLayoutConstraint(
item: tvc2.view,
attribute: .Top,
relatedBy: .Equal,
toItem: tvc.view,
attribute: .Top,
multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(
item: tvc2.view,
attribute: .Bottom,
relatedBy: .Equal,
toItem: tvc.view,
attribute: .Bottom,
multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(
item: tvc2.view,
attribute: .Width,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1, constant: 400))
self.view.addConstraint(NSLayoutConstraint(
item: tvc2.view,
attribute: .Left,
relatedBy: .Equal,
toItem: tvc.view,
attribute: .Right,
multiplier: 1, constant: 0))
}
This line,
tvc2.view.setTranslatesAutoresizingMaskIntoConstraints(false)
should be uncommented. When you add a view programmatically, and you're using constraints, you should always set that to false. On the other hand, you should not set that to false for controller's main view.
Also, the width constraint, since it only applies to tvc2, should be added to tvc2, not to self.view (although it should work either way).
It's also a bit odd, that you're adding one view using frames, and the other with constraints. It would be better to do both using the same paradigm.

Resources