Auto layout not set properly when give equal width constrains to buttons - ios

I see many solution and applied auto layout constrains but still i am not getting desired solution.
Current Result:
Constrains given to all button:
View Hierarchy:

Assuming that what do you want is letting the 3 buttons equal each others and filling the width of screen using the Interface Builder (with no code, i.e: not programmatically), this is a solution:
you don't need to add them in views. just follow these steps:
let's start with this:
note that the buttons don't have any constraints -yet-, just add them to the to the bottom of the view (or where ever you want to display them, for this solution, I will display them in bottom of the screen). Make sure that the 3 of them have the same size.
Adding constraints to the orange button:
add the following constraints:
leading, bottom space, and equal height.
Adding constraints to the blue button:
add the following constraints:
trailing, bottom space and equal height.
Adding constraints to the cyan button:
ctrl + drag from the cyan button to the orange button and add the following constraints: horizontal spacing and center vertically.
ctrl + drag from the cyan button to the blue button and add the following constraints: horizontal spacing.
bottom space and equal height.
So far so good, we are almost done!
Now, select the 3 buttons and add the following constraint: equal widths.
Your buttons should look like this:
Now, all you have to do is select each of the two horizontal spacing constraints and set their constants to 0 -from the size inspector-:
AND there you go:
I hope this helped, Cheers Up.

Sorry for solution in code.. .Swift 3.0:
let button1 = UIButton()
let button2 = UIButton()
let button3 = UIButton()
let buttons = [button1, button2, button3]
button1.backgroundColor = .red
button2.backgroundColor = .blue
button3.backgroundColor = .green
buttons.forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
buttons.forEach {
let cnt1 = NSLayoutConstraint(item: $0, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 1, constant: 0)
let cnt2 = NSLayoutConstraint(item: $0, attribute: .height, relatedBy: .equal, toItem: view, attribute: .height, multiplier: 0.2, constant: 0)
view.addConstraints([cnt1, cnt2])
}
let cnt1 = NSLayoutConstraint(item: button1, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0)
let cnt2 = NSLayoutConstraint(item: view, attribute: .trailing, relatedBy: .equal, toItem: button3, attribute: .trailing, multiplier: 1, constant: 0)
let cnt3 = NSLayoutConstraint(item: button2, attribute: .width, relatedBy: .equal, toItem: button1, attribute: .width, multiplier: 1, constant: 0)
let cnt4 = NSLayoutConstraint(item: button3, attribute: .width, relatedBy: .equal, toItem: button2, attribute: .width, multiplier: 1, constant: 0)
let cnt5 = NSLayoutConstraint(item: button2, attribute: .leading, relatedBy: .equal, toItem: button1, attribute: .trailing, multiplier: 1, constant: 0)
let cnt6 = NSLayoutConstraint(item: button3, attribute: .leading, relatedBy: .equal, toItem: button2, attribute: .trailing, multiplier: 1, constant: 0)
view.addConstraints([cnt1, cnt2, cnt3, cnt4, cnt5, cnt6])

Are you trying to lay out three buttons horizontally that have equal height and width? I think you can achieve this without using UIViews. I set constraints like as follows.
This is a simulator screenshot.

Related

How to programmatically expand/collapse Vertical UIViews build with constraints?

i'm not very familiar with autolay-out and constraints.
I have build a view with 7 subviews which are build with constraints to fit landscape and portait mode. Everything is fine (see picture).
Here under a sample code used for the 6th subview.
The width constraint has mediumPriority and the others (left, top, bottom) have highPriority.
6View = UIView()
mainScrollView.addSubview(6View)
6View.backgroundColor = UIColor(red: 120/255, green: 120/255, blue: 120/255, alpha: 1.0)
6View.translatesAutoresizingMaskIntoConstraints = false
// constraints
let topConstraint = NSLayoutConstraint(item: 6View, attribute: .top, relatedBy: .equal, toItem: mainScrollView, attribute: .top, multiplier: 1, constant: kMainMargin)
topConstraint.priority = highContraintPriority
mainScrollView.addConstraint(topConstraint)
let leftConstraint = NSLayoutConstraint(item: 6View, attribute: .left, relatedBy: .equal, toItem: day5View, attribute: .right, multiplier: 1, constant: kMainMargin)
leftConstraint.priority = highContraintPriority
mainScrollView.addConstraint(leftConstraint)
let bottomConstraint = NSLayoutConstraint(item: 6View, attribute: .bottom, relatedBy: .equal, toItem: mainScrollView, attribute: .bottom, multiplier: 1, constant: kMainMargin)
bottomConstraint.priority = highContraintPriority
mainScrollView.addConstraint(bottomConstraint)
let widthConstraint = NSLayoutConstraint(item: 6View, attribute: .width, relatedBy: .equal, toItem: mainScrollView, attribute: .width, multiplier: (1/7), constant: -1)
widthConstraint.priority = mediumContraintPriority
mainScrollView.addConstraint(widthConstraint)
Now i'd like when i touch any of the 7 views to see the touched view to expand (double size) and the other 6 views to collapse.
How would you achieve this ?
Thanks a lot for your help.
Best regards.
Make a reference to the width constraint for every view and when clicked do
widthConstraint.constant = // expanded value
and set this for all the other views
otherwidthConstraint.constant = 0
then call
self.view.layoutIfNeeded()
You may also put all these constraints inside an array ( var arrOfAllWidths = [NSLayoutConstraint]()) and do this
arrOfAllWidths.forEach { $0.constant = 0 }
then get the index of the clicked view say it's view 0
arrOfAllWidths[0].constant = // expanded value
Also no need for the priorities , and don't forget to set this when give constraints to a view programmatically
view6.translatesAutoresizingMaskIntoConstraints = false

Wrong NSLayoutConstraint Causes Black Screen

I want to place header view on top of screen with NSLayoutConstraint (I must use NSLayoutConstraint). When I do it like in below code, view places corruptly in somewhere else and also controllers background color turns black and nothing works. Where am I doing wrong?
I searched below posts for not opening a duplicate post but nothing fixed it:
Programmatically creating constraints bound to view controller margins
Programmatically Add CenterX/CenterY Constraints
EDIT: This controller is inside navigation controller but I'm not sure If It is related.
override func viewDidLoad(){
self.view.backgroundColor = UIColor.white
boxView.backgroundColor = Color.Common.welcomeScreenBackgroundColor.withAlphaComponent(0.5)
boxView.translatesAutoresizingMaskIntoConstraints = false
self.view.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubView(boxView)
}
override func viewDidLayoutSubviews() {
//Header = 20 from left edge of screen
let cn1 = NSLayoutConstraint(item: boxView, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1.0, constant: 0)
//Header view trailing end is 20 px from right edge of the screen
let cn2 = NSLayoutConstraint(item: boxView, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailing, multiplier: 1.0, constant: 0)
//Header view height = constant 240
let cn3 = NSLayoutConstraint(item: boxView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant:240)
//Header view vertical padding from the top edge of the screen = 20
let cn5 = NSLayoutConstraint(item: boxView, attribute: .top, relatedBy: .equal, toItem: self.topLayoutGuide, attribute: .bottom, multiplier: 1.0, constant: 0)
self.view.addConstraints([cn1,cn2,cn3,cn5])
}
The problem was setting translatesAutoresizingMaskIntoConstraints to false on Superview. So I deleted the;
self.view.translatesAutoresizingMaskIntoConstraints = false
and this solves the problem. I think this causes app creates constraint for superview.

Make UILabel Constraint to display full width

I'm struggling to implement to make my uilabel to stretch from the left side to right side (Full Width), I can do it easily in storyboard but I have a part on my app that needs to display a uilabel progmatically.
Here's my code:
textLabel.backgroundColor = UIColor.grayColor()
textLabel.text = StringsLabel.NOINTERNET
textLabel.textAlignment = .Center
textLabel.font = UIFont.boldSystemFontOfSize(24.0)
textLabel.translatesAutoresizingMaskIntoConstraints = false
centerView.addSubview(textLabel)
let centreXTopLabel:NSLayoutConstraint = NSLayoutConstraint(item: textLabel, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: centerView, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0);
centerView.addConstraint(centreXTopLabel)
Here's the screenshot of what i've got:
Pin it to the leading and trailing of the view at 0 points:
let leading = NSLayoutConstraint(item: textLabel, attribute: .Leading, relatedBy: .Equal, toItem: centerView, attribute: .Leading, multiplier: 1, constant: 0)
let trailing = NSLayoutConstraint(item: textLabel, attribute: .Trailing, relatedBy: .Equal, toItem: centerView, attribute: .Trailing, multiplier: 1, constant: 0)
NSLayoutConstraint.activateConstraints([leading, trailing])
Note that this will result in an ambiguous layout — you need to add a constraint that positions the label vertically.
Try this..
let views = ["textLabel" : textLabel]
let formatString = "|-[textLabel]-|"
let constraints = NSLayoutConstraint.constraintsWithVisualFormat(formatString, options:.AlignAllTop , metrics: nil, views: views)
NSLayoutConstraint.activateConstraints(constraints)

Horizontal UIScrollView autolayout constraint localization

I have multiple buttons separated by some 10px padding (leadingPadding), in case of English the items must start from left to right, and in case of Arabic from right to left and centralized.
This is the code used to draw the scrollView:
var previousView: UIView = self.itemsScrollView!
for i in 0..<self.items.count {
let sectionButton = UIButton(frame: CGRectZero)
sectionButton.titleLabel.text = "i = " + i
sectionButton.translatesAutoresizingMaskIntoConstraints = false
self.itemsScrollView!.addSubview(sectionButton)
// Left Constraint
let leftConstraint = NSLayoutConstraint(
item: sectionButton,
attribute: .Leading,
relatedBy: .Equal,
toItem: toView,
attribute: (toView === self.itemsScrollView!) ? .Leading : .Trailing,
multiplier: 1.0,
constant: leadingPadding);
self.itemsScrollView! .addConstraint(leftConstraint)
// Top Constraint
let topConstraint = NSLayoutConstraint(
item: sectionButton,
attribute: .Top,
relatedBy: .Equal,
toItem: self.itemsScrollView!,
attribute: .Top,
multiplier: 1.0,
constant: self.itemPaddingTopBottom);
self.itemsScrollView! .addConstraint(topConstraint)
// Width Constraint
let widthConstraint = NSLayoutConstraint(
item: sectionButton,
attribute: .Width,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1.0,
constant: buttonWidth);
self.itemsScrollView! .addConstraint(widthConstraint)
// Height Constraint
let heightConstraint = NSLayoutConstraint(
item: sectionButton,
attribute: .Height,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1.0,
constant: itemHeight);
self.itemsScrollView! .addConstraint(heightConstraint)
previousView = sectionButton
}
// add Trailing constraint on scroll
if (previousView !== self.itemsScrollView!) {
let leftConstraint = NSLayoutConstraint(
item: previousView,
attribute: .Trailing,
relatedBy: .Equal,
toItem: self.itemsScrollView!,
attribute: .Trailing,
multiplier: 1.0,
constant: 0.0);
self.itemsScrollView! .addConstraint(leftConstraint)
}
It works great when the language orientation is LTR.
And a weird behavior when the language orientation is RTL (They need to be centralized like the picture above)
I'm assuming this is something related to the Trailing constraint with the scrollView but I'm not sure. If yes, is there any way to fix that?
Thanks
You should not add multiple views directly in scrollview. Instead you should add a single container view ,horizontally centered in scrollview, which should include all the necessary subview. The width of the container view must be determined by its subviews, so the first item leading is aligned with container leading and the last item trailing is aligned with container trailing in LTR mode or first item trailing is aligned with container trailing and last item leading is aligned with container leading in RTL mode

How to programatically add constraints on uinavigationbar subview

I'm adding a subview to navigationbar , problem is that im unable to add constraints to it .Im getting crash like this
terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with items ; value: 0.000000> and > because they have no common ancestor. Does the constraint reference items in different view hierarchies? That's illegal.'
The code used is below
//create a slider and add it to the view
let slider = UISlider()
self.navigationController?.navigationBar.addSubview(slider)
//pin the slider 20 points from the left edge of the the superview
//from the left edge of the slider to the left edge of the superview
//superview X coord is at 0 therefore 0 + 20 = 20 position
let horizonalContraints = NSLayoutConstraint(item: slider, attribute:
.LeadingMargin, relatedBy: .Equal, toItem: view,
attribute: .LeadingMargin, multiplier: 1.0,
constant: 20)
//pin the slider 20 points from the right edge of the super view
//negative because we want to pin -20 points from the end of the superview.
//ex. if with of super view is 300, 300-20 = 280 position
let horizonal2Contraints = NSLayoutConstraint(item: slider, attribute:
.TrailingMargin, relatedBy: .Equal, toItem: view,
attribute: .TrailingMargin, multiplier: 1.0, constant: -20)
//pin 100 points from the top of the super
let pinTop = NSLayoutConstraint(item: slider, attribute: .Top, relatedBy: .Equal,
toItem: view, attribute: .Top, multiplier: 1.0, constant: 100)
//when using autolayout we an a view, MUST ALWAYS SET setTranslatesAutoresizingMaskIntoConstraints
//to false.
slider.translatesAutoresizingMaskIntoConstraints = false
slider.backgroundColor = UIColor.redColor()
//IOS 8
//activate the constrains.
//we pass an array of all the contraints
NSLayoutConstraint.activateConstraints([horizonalContraints, horizonal2Contraints,pinTop])
The above code works fine if i use the line view.addSubview(slider)
instead of
self.navigationController?.navigationBar.addSubview(slider)
But the idea is to add constraints on a subview on navigation bar .
Any thoughts are welcome
As the exception already stated, the navigationBar is not a subview of 'view'. It belongs to the navigationcontroller.
What you could do is to use the navbar's superview:
let slider = UISlider()
self.navigationController?.navigationBar.addSubview(slider)
let targetView = self.navigationController?.navigationBar.superview
let horizonalContraints = NSLayoutConstraint(item: slider, attribute:
.LeadingMargin, relatedBy: .Equal, toItem: targetView,
attribute: .LeadingMargin, multiplier: 1.0,
constant: 20)
let horizonal2Contraints = NSLayoutConstraint(item: slider, attribute:
.TrailingMargin, relatedBy: .Equal, toItem: targetView,
attribute: .TrailingMargin, multiplier: 1.0, constant: -20)
let pinTop = NSLayoutConstraint(item: slider, attribute: .Top, relatedBy: .Equal,
toItem: targetView, attribute: .Top, multiplier: 1.0, constant: 10)
slider.translatesAutoresizingMaskIntoConstraints = false
slider.backgroundColor = UIColor.redColor()
NSLayoutConstraint.activateConstraints([horizonalContraints, horizonal2Contraints,pinTop])
That removes the exception and might look like it does what you want, but it is definitely not a good solution. If you want the slider inside the navbar, add it to the navigationitem instead. If you want it bellow the navbar, add it to your View and set a constraint to the top layout guide.

Resources