Adding NSLayoutConstraints to UISegmentedControl causes UISegmentedControl to disappear - swift

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.

Related

How we set constraint for adding subview

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.

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

converting storyboard constraints into code

I'm trying to programmatically set up constraints for a class in my app so that when the keyboard appears everything moves up. i already got it working before in the storyboard, but when i take what it says in the size inspector and write it in code format, it doesn't work as expected.
...
//memo area
var memoArea = UITextView(frame: CGRectMake(20, 291, 275, 225))
memoArea.backgroundColor = majorColor
memoArea.delegate = self
self.view.addSubview(memoArea)
var memoLine = customShadow(theself: self.view, frame: memoArea.frame)
//Spacer View
var spacer:UIView = UIView(frame: CGRectMake(84, 518, 160, 6))
spacer.alpha = 0
self.view.addSubview(spacer)
//Constraints
var memoAreaToSpacer:NSLayoutConstraint = NSLayoutConstraint(item: spacer, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: memoArea, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 8)
spacerToBottom = NSLayoutConstraint(item: bottomLayoutGuide, attribute: NSLayoutAttribute.Top, relatedBy: .Equal, toItem: spacer, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0)
view.addConstraint(memoAreaToSpacer)
view.addConstraint(spacerToBottom)
...
so when a keyboard fires a notification this happens.
func updateBottomLayoutConstraintWithNotification(notification: NSNotification) {
let userInfo = notification.userInfo!
let animationDuration = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as NSNumber).doubleValue
let keyboardEndFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as NSValue).CGRectValue()
let convertedKeyboardEndFrame = view.convertRect(keyboardEndFrame, fromView: view.window)
let rawAnimationCurve = (notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as NSNumber).unsignedIntValue << 16
let animationCurve = UIViewAnimationOptions.init(UInt(rawAnimationCurve))
let frame = self.tabBarController?.tabBar.frame
let height = frame?.size.height
spacerToBottom.constant = CGRectGetMaxY(view.bounds) - CGRectGetMinY(convertedKeyboardEndFrame) - height! - 5
UIView.animateWithDuration(animationDuration, delay: 0.0, options: .BeginFromCurrentState | animationCurve, animations: {
self.view.layoutIfNeeded()
}, completion: nil)
}
Here's the full app https://github.com/stanchiang/phoneHub
The code i'm talking about is in the BaseDetailViewController.swift in viewDidLoad().
The constraints i'm transferring from is the Edit Controller in the Main.storyboard file.
also, for reference i got it working through storyboard through this blog post.
http://effortlesscode.com/auto-layout-keyboard-shown-hidden/
thanks for your answers, still trying to get the hang of autolayout so general tips are welcome too.
I'm no Auto Layout expert, but I'll share what I know:
You need to turn off the AutoresizingMask for the views that you are adding constraints to:
view.setTranslatesAutoresizingMaskIntoConstraints(false)
spacer.setTranslatesAutoresizingMaskIntoConstraints(false)
memoArea.setTranslatesAutoresizingMaskIntoConstraints(false)
You are going to need a lot more constraints. I only see 2 in your code. You will need to fully constrain your layout. This includes the settings you are specifying right now with frames (width, height). Positions should probably be specified relative to other objects if you want everything to move when the keyboard shows up. When you got this working in the Storyboard, there had to be more than just 2 constraints for the entire ViewController. You'll need to bring over all of the constraints.
I suggest you add the following 6 constraints to replace the values you were setting with the frames. Put these with your other constraints. I've added the view frames in comments so that you can see where I got the values:
//var memoArea = UITextView(frame: CGRectMake(20, 291, 275, 225))
memoArea.addConstraint(NSLayoutConstraint(item: memoArea, attribute: .Width, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 275.0))
memoArea.addConstraint(NSLayoutConstraint(item: memoArea, attribute: .Height, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 225.0))
self.view.addConstraint(NSLayoutConstraint(item: memoArea, attribute: .Leading, relatedBy: .Equal,
toItem: self.view, attribute: .Leading, multiplier: 1.0, constant: 20.0))
// var spacer:UIView = UIView(frame: CGRectMake(84, 518, 160, 6))
spacer.addConstraint(NSLayoutConstraint(item: spacer, attribute: .Width, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 160.0))
spacer.addConstraint(NSLayoutConstraint(item: spacer, attribute: .Height, relatedBy: .Equal,
toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 6.0))
self.view.addConstraint(NSLayoutConstraint(item: spacer, attribute: .Leading, relatedBy: .Equal,
toItem: self.view, attribute: .Leading, multiplier: 1.0, constant: 84.0))