How we set constraint for adding subview - swift

I want to add view on my View, but they are not set well this width stretch. I am new to iOS. Any help would be appreciated. This is what I get when I try to do what I want, can someone help to fix this:
openView.translatesAutoresizingMaskIntoConstraints = false
let topConstraint = NSLayoutConstraint(item: openView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: openView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0)
let leadingConstraint = NSLayoutConstraint(item: openView, attribute: .leading, relatedBy: .equal, toItem: self, attribute: .leading, multiplier: 1, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: openView, attribute: .trailing, relatedBy: .equal, toItem: self, attribute: .trailing, multiplier: 1, constant: 0)
self.view.addSubview(openView)
openView.addConstraints([topConstraint, bottomConstraint, leadingConstraint, trailingConstraint])
openView.layoutIfNeeded()

In your xcode open main.Storyboard --> Open your viewController -->Add UIView --> Make your view frame like you want --> And add constraints
Ex: If you want width=600 & height=600, set frame like you want and then assign constraints.
.Refer linked image for reference

Here you are setting subview's frame as the initial frame of controller view. ie, the controller's view will be fully covered by subview.
If you want to add constraints,
1) Take the storyboard or xib.
2) Tap on the UI element.
3) Tap on the constraints button as shown in image.
4) Set the values from each border.
You can also set height, width such like parameters. The whole need is the system should be able to interpret the accurate position from the constraints.

Related

How to put a uiview back in a stackview after I took it out?

I have a viewcontroller that holds multiple stackviews. There is a button that when pressed, the corresponding uiview wil become fullscreen inside the original view. There is a different button that is supposed to make the uiview go back to its original stackview. The uiview itself contains other views. I am having some problems doing that. The uiview does end up in the stackview, but not near the same size/place it used to be. I am not sure how to solve this, and been going at it for several hours now, looking at multiple sources.
This is the code that makes the uiview go fullscreen:
private func moveToFrontOfCardView(v: UIView) {
originalView = v.superview
if let stack = originalView as? UIStackView {
stack.removeArrangedSubview(v)
}
myCardView.addSubview(v)
let topConstraint = NSLayoutConstraint(item: v, attribute: .top, relatedBy: .equal, toItem: myCardView, attribute: .top, multiplier: 1, constant: 10)
let bottomConstraint = NSLayoutConstraint(item: v, attribute: .bottom, relatedBy: .equal, toItem: myCardView, attribute: .bottom, multiplier: 1, constant: -10)
let leftConstraint = NSLayoutConstraint(item: v, attribute: .left, relatedBy: .equal, toItem: myCardView, attribute: .left, multiplier: 1, constant: 10)
let rightConstraint = NSLayoutConstraint(item: v, attribute: .right, relatedBy: .equal, toItem: myCardView, attribute: .right, multiplier: 1, constant: -10)
myCardView.addConstraints([topConstraint, bottomConstraint, leftConstraint, rightConstraint])
}
And this is the code I use when I want it to go back:
private func moveToOriginalPosition(v: UIView) {
if let stack = originalView as? UIStackView {
stack.addArrangedSubview(v)
}
}
Does anyone have a clue how I could fix this?
EDIT
I've tried Saqib and Bilals answer, but I get this as a result:
Declare a class variable for tracking view's index
var selectedIndex = 0 // Contains Current Seleceted view's index
overrie func viewDidLoad() { ...
Before removing view from stackview get the view index like this selectedIndex = stack.subviews.index(of: v)
keep reference to all the constraints.
Before adding it back disable all the constraints topConstraint.isActive = false
Now add the view at the same index using stack.insertArrangedSubview(view, at: selectedIndex)
An other option is to create a same new view and just hide/unhide the one in stackview. StackView automatically fills the space accordingly for the hidden views.
You should deActivate the constraints you added to view when removed it from stackView, at the time you want add the view to the stackView again.
For this you should make the constraints instance of your viewController class and next, write your moveToOriginalPosition(v: UIView) method like this:
private func moveToOriginalPosition(v: UIView) {
if let stack = originalView as? UIStackView {
stack.addArrangedSubview(v)
topConstraint.isActive = false
bottomConstraint.isActive = false
leftConstraint.isActive = false
rightConstraint.isActive = false
}
}
Ofcourse, you should remove, these lines of codes from moveToFrontOfCardView(v: UIView) method:
self.topConstraint = NSLayoutConstraint(item: v, attribute: .top, relatedBy: .equal, toItem: myCardView, attribute: .top, multiplier: 1, constant: 10)
self.bottomConstraint = NSLayoutConstraint(item: v, attribute: .bottom, relatedBy: .equal, toItem: myCardView, attribute: .bottom, multiplier: 1, constant: -10)
self.leftConstraint = NSLayoutConstraint(item: v, attribute: .left, relatedBy: .equal, toItem: myCardView, attribute: .left, multiplier: 1, constant: 10)
self.rightConstraint = NSLayoutConstraint(item: v, attribute: .right, relatedBy: .equal, toItem: myCardView, attribute: .right, multiplier: 1, constant: -10)
myCardView.addConstraints([topConstraint, bottomConstraint, leftConstraint, rightConstraint])
and add them where you make your view initialized. and replace below lines with above lines in moveToFrontOfCardView(v: UIView) method:
topConstraint.isActive = true
bottomConstraint.isActive = true
leftConstraint.isActive = true
rightConstraint.isActive = true
By the looks of things you don't need to remove the original view. You could make a copy of it then display the copy full screen. Then when you dismiss this copy you release the reference to it

How to center multiple elements inside UIView

I'm trying to center multiple elements (labels and buttons) inside UIView (or UIStackView). I tried adding elements to UIStackView with Center Alignment and Distribution Fill and Spacing 10, but my elements keeps getting positioned from the left side. I tried changing centers. Still no luck.
What i'm trying to achieve:
[-------label--button-------] With 2 elements.
[--label--button--button--] With 3 elements.
Is there any way of achieving this without using spacers. Theoretically I maybe could use constrains, but i'm not sure it's a good idea because of changing number of elements.
let simpleView = UIView()
simpleView.addSubview(text)
simpleView.addSubview(button)
let leftLeading = NSLayoutConstraint(item: text, attribute:
.LeadingMargin, relatedBy: .Equal, toItem: simpleView,
attribute: .LeadingMargin, multiplier: 1.0,
constant: 10)
let leftLeading2 = NSLayoutConstraint(item: button, attribute:
.LeadingMargin, relatedBy: .Equal, toItem: simpleView,
attribute: .LeadingMargin, multiplier: 1.0,
constant: 10)
simpleView.addConstraint(leftLeading)
simpleView.addConstraint(leftLeading2)
simpleView.translatesAutoresizingMaskIntoConstraints = false
simpleView.sizeToFit()
newView.addSubview(simpleView)
let horizontalConstraint = NSLayoutConstraint(item: simpleView, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: newView, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0)
newView.addConstraint(horizontalConstraint)
let verticalConstraint = NSLayoutConstraint(item: simpleView, attribute: NSLayoutAttribute.CenterY, relatedBy: NSLayoutRelation.Equal, toItem: newView, attribute: NSLayoutAttribute.CenterY, multiplier: 1, constant: 0)
newView.addConstraint(verticalConstraint)
I must be doing something wrong because this is what I get (newView is red, button is blue, text label is missing) :
Create a view, center the view where you need to. Then inside that view you can insert each element and give each one a programatic NSLayoutConstraint that has a .Leading of 5 or whatever spacing you want between elements.
VIEW[ELEMENT ELEMENT ELEMENT]
The number will not matter. Look into NSLayoutConstraints and make sure you use view.addConstraints.

Programmatically creating constraints bound to view controller margins

I'm trying to make a view that will act as a sort of "panel", attached to the right side of the view controller.
That is, it is bound to the trailing, top, and bottom margins of the parent view controller, with a static width of 300
However, I just can't seem to get it right, I'm either breaking a constraint or doing something xcode tells me is illegal.
What am I doing wrong?
Here is the code in the controller
let myView = UIView()
view.backgroundColor = UIColor.redColor()
self.view.addSubview(view)
let topConstraint = NSLayoutConstraint(item: myView,
attribute: .Top,
relatedBy: .Equal,
toItem: self.topLayoutGuide,
attribute: .Bottom,
multiplier: 1,
constant: 0)
let trailingConstraint = NSLayoutConstraint(item: self.view,
attribute: .TrailingMargin,
relatedBy: .Equal,
toItem: myView,
attribute: .Trailing,
multiplier: 1,
constant: 0)
let bottomConstraint = NSLayoutConstraint(item: self.bottomLayoutGuide,
attribute: .Top,
relatedBy: .Equal,
toItem: myView,
attribute: .Bottom,
multiplier: 1,
constant: 0)
let widthConstraint = NSLayoutConstraint(item: myView,
attribute: .Width,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1,
constant: 300)
self.view.addConstraints([trailingConstraint])
view.addConstraints([topConstraint, bottomConstraint, widthConstraint])
Actually the problem in your code is that you did not set the translatesAutoresizingMaskIntoConstraints of myview to false, whenever you want to use auto-layout constraints then you have to set translatesAutoresizingMaskIntoConstraints of a view to false.
Another Problem is that you do not add myview on self.view I have updated your code and Its working fine According your constraints.
Put below code in your ViewController .
let myView = UIView()
myView.backgroundColor = UIColor.redColor()
self.view.addSubview(myView)
myView.translatesAutoresizingMaskIntoConstraints = false
view.addConstraint(NSLayoutConstraint(item: myView, attribute: .Top, relatedBy: .Equal, toItem: self.topLayoutGuide, attribute: .Bottom, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: myView, attribute: .Bottom, relatedBy: .Equal, toItem: self.bottomLayoutGuide, attribute:.Top, multiplier: 1, constant: 20))
view.addConstraint(NSLayoutConstraint(item: myView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute,multiplier: 1, constant: 300))
view.addConstraint(NSLayoutConstraint(item: myView, attribute: .TrailingMargin, relatedBy: .Equal, toItem: view, attribute: .TrailingMargin, multiplier: 1, constant: 0))
In your example code above, it seems like you are mixing up view and myView in a few places. In any event, widthConstraint should be added to myView and topConstraint, trailingConstraint, and bottomConstraint should be added to self.view. The reason for this is constraints must be added to the closest superview ancestor that lays out both views involved in the constraint. In the case where you are constraining a child view attribute to an attribute on its parent view, the constraint must be added to the parent view, since it lays out both itself and the child view. If you have a constraint between two sibling views, the constraint would be added to their parent view, since it is the closest ancestor that lays out both the views involved.
If you're able to target iOS 9.0 and above, it's much cleaner and easier to use the new NSLayoutAnchor and NSLayoutDimension API for creating these kinds of constraints. It also provides strict type checking and the compiler can verify correctness. With these new APIs, your example code would simply become:
let myView = UIView()
myView.backgroundColor = UIColor.redColor()
self.view.addSubview(myView)
let margins = self.view.layoutMarginsGuide
myView.trailingAnchor.constraintEqualToAnchor(margins.trailingAnchor).active = true
myView.topAnchor.constraintEqualToAnchor(margins.topAnchor).active = true
myView.bottomAnchor.constraintEqualToAnchor(margins.bottomAnchor).active = true
myView.widthAnchor.constraintEqualToConstant(300.0).active = true
No need to explicitly add the constraints to the right view, etc. You can read more about this method of creating constraints here:
https://developer.apple.com/library/ios/documentation/AppKit/Reference/NSLayoutAnchor_ClassReference/
and here:
https://developer.apple.com/library/ios/documentation/AppKit/Reference/NSLayoutDimension_ClassReference/
There seems to be some ambiguity in your code, you are creating a UIView as myView but adding view to self.view and even constraint also to view itself. So correct your code and replace view with myView.
Secondly setTranslayesAutoresizingMaskIntoConstraints to false.
Then add all the constraints to self.view. This should solve your problem.
myView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addConstraints([trailingConstraint, bottomConstraint, widthConstraint])
VFL is also a better and clean approach. It actually gives a visualization of how constraint are setup.

How to add equal width and equal height constraints programmatically in swift?

Could anyone help me I have this simple view that is very simple to do if I'm using story board but all of my UILabel is based on somewhere(API) that is why I need to create my view programmatically.
this is the view I would like to achieved
what I accomplished at the moment are pinning the top label in its position and adding leading space on label 1, 2, & 3
basically what i need are:
pin the label 1 to the top label
pin the label 3 to bottom
add vertical spaces to label 1,2,3
have equal width and height constraint in label 1,2 ,3
this is my code so far.
private func addTopConstraint(from:AnyObject,to:AnyObject,cons:CGFloat){
let topConstraint = NSLayoutConstraint(item: from, attribute: .Top, relatedBy: .Equal, toItem: to, attribute: .Bottom, multiplier: 1.0, constant: cons)
self.view.addConstraint(topConstraint)
}
private func addVerticalSpace(from:AnyObject,to:AnyObject,cons:CGFloat){
let verticalSpace = NSLayoutConstraint(item: from, attribute: .Bottom, relatedBy: .Equal, toItem: to, attribute: .Bottom, multiplier: 1.0, constant: cons)
self.view.addConstraint(verticalSpace)
}
private func addLeadingSpace(from:AnyObject,to:AnyObject,cons:CGFloat){
let leadingSpace = NSLayoutConstraint(item: from, attribute: .Leading, relatedBy: .Equal, toItem: to, attribute: .Leading, multiplier: 1.0, constant: cons)
self.view.addConstraint(leadingSpace)
}
private func sameWidthAndHeight (from:AnyObject,to:AnyObject){
let width = NSLayoutConstraint(item: from, attribute: .Width, relatedBy: .Equal, toItem: to, attribute: .Width, multiplier: 1.0, constant: 0)
let height = NSLayoutConstraint(item: from, attribute: .Height, relatedBy: .Equal, toItem: to, attribute: .Height, multiplier: 1.0, constant: 0)
self.view.addConstraint(width)
self.view.addConstraint(height)
}
but the sameWidthAndHeight and addVerticalSpace function fails
maybe it something to do with content hugging and priorities too, I'm not that sure since we're used to do this in storyboard.
could anyone share a thought? Thanks in advance

Adding NSLayoutConstraints to UISegmentedControl causes UISegmentedControl to disappear

I have a UISegmentedControl defined programmatically.
I am trying to add a layout constraint so that when my iPad rotates, the segmented control sizes correctly within the rotated view rather than spill off screen.
I apply the following constraint:
streamSegmentedControl.translatesAutoresizingMaskIntoConstraints = false
let segmentedControlWidth = NSLayoutConstraint(item: streamSegmentedControl,
attribute: .Width,
relatedBy: .Equal,
toItem: self.containerView,
attribute: .Width,
multiplier: 1.0,
constant: -10.0)
containerView.addConstraint(segmentedControlWidth)
My UIsegmentControl is defined as follows:
streamSegmentedControl = UISegmentedControl(items: ["Today's Events", "Past Events"])
streamSegmentedControl.frame = CGRectMake(-10,containerView.frame.size.height*0.3,containerView.frame.width+20,40)
streamSegmentedControl.selectedSegmentIndex = 0
streamScope = "today"
streamSegmentedControl.setTitleTextAttributes(segmentedControlFont as [NSObject : AnyObject], forState: .Normal)
streamSegmentedControl.backgroundColor = UIColor.colorFromClass("background")
streamSegmentedControl.tintColor = UIColor.colorFromClass("default")
streamSegmentedControl.addTarget(self, action: "changeStreamScope:", forControlEvents: UIControlEvents.ValueChanged)
containerView.addSubview(streamTableView)
containerView.addSubview(streamSegmentedControl)
I don't get an error, but at runtime, my segmented control disappears. Not sure what I am missing here as I've only done auto layout within storyboards in the past.
I only want to be able to adjust the width of the segmented control, so I assume I only need a single layout constraint.
Can anyone give me some direction? Thanks.
You need to add three constraints at least. Leading, top and trailing.
I am weak in swift but your constraints should look like.
let segmentedControlTop = NSLayoutConstraint(item: streamSegmentedControl,
attribute: .Top,
relatedBy: .Equal,
toItem: self.containerView,
attribute: .Top,
multiplier: 1.0,
constant: 0.0)
let segmentedControlLeading = NSLayoutConstraint(item: streamSegmentedControl,
attribute: .Leading,
relatedBy: .Equal,
toItem: self.containerView,
attribute: .Leading,
multiplier: 1.0,
constant: 0.0)
let segmentedControlTrailing = NSLayoutConstraint(item: streamSegmentedControl,
attribute: .Trailing,
relatedBy: .Equal,
toItem: self.containerView,
attribute: .Trailing,
multiplier: 1.0,
constant: 0.0)
containerView.addConstraint(segmentedControlTop)
containerView.addConstraint(segmentedControlLeading)
containerView.addConstraint(segmentedControlTrailing)
If you want to keep a 5 point offset from superview, try playing with constant values of leading and trailing using 5 points.