I am manually adding a bunch of layout constraints that are for portrait mode. Now I want to make two sets of constraints, and flip between them based on whether the device is in landscape or portrait mode. I do not want to use "visualformatting".
My existing/working constraint looks like this:
mainview.addConstraint( NSLayoutConstraint(item: vPic, attribute: .Top, relatedBy: .Equal, toItem: mainview, attribute: .Top, multiplier: 1, constant: 0) )
But I want to put the constraint in to an array (along with others), and apply them when the device rotates:
var constraintsPortrait = [NSLayoutConstraint]()
constraintsPortrait.extend(NSLayoutConstraint(item: vPic, attribute: .Top, relatedBy: .Equal, toItem: mainview, attribute: .Top, multiplier: 1, constant: 0))
mainview.addConstraints(constraintsPortrait)
I get an error on the 2nd line:
Type 'NSLayoutConstraint' does not conform to protocol 'SequenceType'
The only examples I can find on the web, are for how to create the constraint as an object, using the "visualformat" syntax. Like this:
let view2_constraint_V:NSArray = NSLayoutConstraint.constraintsWithVisualFormat("V:[view2(>=40)]", options: NSLayoutFormatOptions(0), metrics: nil, views: viewsDictionary)
Any suggestions?
extend is to concat Array(or arbitrary SequenceType) to Array. You should use append instead.
var constraintsPortrait = [NSLayoutConstraint]()
constraintsPortrait.append(NSLayoutConstraint(item: vPic, attribute: .Top, relatedBy: .Equal, toItem: mainview, attribute: .Top, multiplier: 1, constant: 0))
mainview.addConstraints(constraintsPortrait)
But in your case, you don't have to prepare an array for this. You can directly add a constraint with addConstraint method.
mainview.addConstraint(NSLayoutConstraint(item: vPic, attribute: .Top, relatedBy: .Equal, toItem: mainview, attribute: .Top, multiplier: 1, constant: 0))
Adding constraints in code is painful. There are third-party libraries out there that makes the process less painful. Here are some of them.
Snappy
cartography
purelayout
Related
When I first load my calendar, the layout become misaligned (refer to image 1). After physically rotating my device to landscape and back to portrait, it fix itself. (refer to image 2)
Anyone have any idea how to fix this issue? I do not know where to start to find the issue that caused this to happen.
Side note: (not sure if this have anything to do with the issue.)
the page before this also have another FSCalendar.
this affected fscalendar is inside a scrollview with constraint set to height = 0.4 of superview, width with left right margin of 8, and center x align to superview.
I had the same issue (and just like for you, rotating the device would fix it).
I was able to fix my problem by changing the layout constraints and setting the FSCalendar's translatesAutoresizingMaskIntoConstraints to false (I had created the FSCalendar programmatically).
In the end, I put the FSCalendar inside another UIView and set it up like this:
calendarView.translatesAutoresizingMaskIntoConstraints = false
calendarContainer.addSubview(calendarView)
[NSLayoutConstraint(item: calendarView, attribute: .top, relatedBy: .equal, toItem: calendarContainer, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: calendarView, attribute: .bottom, relatedBy: .equal, toItem: calendarContainer, attribute: .bottom, multiplier: 1, constant: 0),
NSLayoutConstraint(item: calendarView, attribute: .leading, relatedBy: .equal, toItem: calendarContainer, attribute: .leading, multiplier: 1, constant: 0),
NSLayoutConstraint(item: calendarView, attribute: .trailing, relatedBy: .equal, toItem: calendarContainer, attribute: .trailing, multiplier: 1, constant: 0)
].forEach({ $0.isActive = true })
I am initialising a view infoScreen and adding it as a subview of the screen with its bottom, left and right constraints set like this:
let left = NSLayoutConstraint(item: infoScreen, attribute: .Left, relatedBy: .Equal, toItem: view, attribute: .LeftMargin, multiplier: 1, constant: 0)
let right = NSLayoutConstraint(item: infoScreen, attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .RightMargin, multiplier: 1, constant: 0)
var yConstraint: NSLayoutConstraint!
if (point.y < halfOfScreen) {
yConstraint = NSLayoutConstraint(item: infoScreen, attribute: .Top, relatedBy: .Equal, toItem: highlightedView, attribute: .CenterY, multiplier: 1, constant: radius + stalkLength)
}
else {
// This gets called the first time round.
yConstraint = NSLayoutConstraint(item: infoScreen, attribute: .Bottom, relatedBy: .Equal, toItem: highlightedView, attribute: .CenterY, multiplier: 1, constant: -radius - stalkLength)
}
view.addConstraints([left, right, yConstraint])
as well as it's height being set.
Then after a button within infoScreen is clicked, I'm calling infoScreen.removeFromSuperview().
Then the same function is used to reinitialise infoScreen with different parameters, and add it to the screen. However, this time it has its top constraint set instead of the bottom constraint.
In iOS 9 this works perfectly, however in iOS 8, it acts as if the bottom constraint is still set and messes up the view. If I run the code so that it never has the bottom constraint set (essentially skipping over the first run of the initialisation function), then it works fine in iOS 8. What could be causing this?
I can't seem to find a proper answer on SO. I'm trying to rotate a subclassed UISlider and apply constraints to position it properly, but can't seem to get it to work correctly. In the XIB it has constraints so that I don't have any issues with error showing. (Boss hates errors showing). So I remove the constraints first.
My code looks like this:
removeConstraints([sliderHeight, sliderWidth, sliderLeading, sliderBottom])
let rotation = CATransform3DMakeRotation(-CGFloat(M_PI_2), 0.0, 0.0, 1.0)
self.layer.transform = rotation
let views = ["slider":slider, "deviceIcon":deviceIcon]
var constraints = NSLayoutConstraint.constraintsWithVisualFormat("H:[deviceIcon]-8-[slider]", options: .DirectionLeadingToTrailing, metrics: nil, views: views)
constraints.append(NSLayoutConstraint(item: deviceIcon, attribute: .Bottom, relatedBy: .Equal, toItem: slider, attribute: .Bottom, multiplier: 1, constant: 0))
constraints.append(NSLayoutConstraint(item: slider, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 30))
constraints.append(NSLayoutConstraint(item: slider, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 115))
addConstraints(constraints)
Once it runs, the frames look like this (white area between grey and orange vertical dashes is the thumb) :
It doesn't have the proper width/height and it isn't aligned to the bottom of the deviceIcon on its left. (I think it might be the proper distance from the deviceIcon, I can't tell.)
How can I do this properly?
So the answer is: Don't. Simply adjust the existing leading and bottom constraints so that the rotated slider is in the proper position.
I'm new to AutoLayout and would like to display my UITextField at 100% width with a consistent 15px left and right margin, like so:
Typically I would do this using CGRect, setting the width to the containing view's width minus 30px, then offset the left side by 15px:
searchTextField.frame = CGRectMake(15, 0, view.frame.width - 30, 50)
But I'd like to learn AutoLayout for this sort of thing, since it's the way to go these days. I should note that I am doing everything programmatically -- no Storyboards here.
I'd love it if someone could help me out!
Update
Wow! Thank you for all the responses. I believe all of them would achieve what I'm trying to do, but there can only be one :)
Usually I use for this cocoapod that is dealing with constraints, but if you need pure apple solution documentation states:
You have three choices when it comes to programmatically creating
constraints: You can use layout anchors, you can use the
NSLayoutConstraint class, or you can use the Visual Format Language.
Approach with NSLayoutConstraints in your case would be:
NSLayoutConstraint(item: textField, attribute: .Leading, relatedBy: .Equal, toItem: parentView, attribute: .LeadingMargin, multiplier: 1.0, constant: 15.0).active = true
NSLayoutConstraint(item: textField, attribute: .Trailing, relatedBy: .Equal, toItem: parentView, attribute: .TrailingMargin, multiplier: 1.0, constant: -15.0).active = true
NSLayoutConstraint(item: textField, attribute: .Top, relatedBy: .Equal, toItem: parentView, attribute: .TopMargin, multiplier: 1.0, constant: 50.0).active = true
Remember that if you don't have any constraints in the view, they would be added automatically and you'll have to deal with them and conflicts that would be created by adding new constraints on runtime. To avoid this you could either create textField manually and add it to the view or set constraints with low priority in the Interface Builder .
Assuming the parent of the text field is view, do this after view.addSubview(searchTextField):
NSLayoutConstraint.activateConstraints([
searchTextField.leadingAnchor.constraintEqualToAnchor(view.leadingAnchor, constant: 15),
searchTextField.trailingAnchor.constraintEqualToAnchor(view.trailingAnchor, constant: -15),
])
Use this code:
let topConstraint = NSLayoutConstraint(item: textField, attribute: NSLayoutAttribute.Top, relatedBy: .Equal, toItem: self.view, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0)
let trailingConstraint = NSLayoutConstraint(item: textField, attribute: NSLayoutAttribute.Trailing, relatedBy: .Equal, toItem: self.view, attribute: NSLayoutAttribute.Trailing, multiplier: 1.0, constant: 15)
let leadingConstraint = NSLayoutConstraint(item: textField, attribute: NSLayoutAttribute.Leading, relatedBy: .Equal, toItem: self.view, attribute: NSLayoutAttribute.Leading, multiplier: 1.0, constant: 15)
let heightConstraint = NSLayoutConstraint(item: textField, attribute: NSLayoutAttribute.Height, relatedBy: .Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 50)
self.view.addConstraint(topConstraint )
self.view.addConstraint(trailingConstraint)
self.view.addConstraint(leadingConstraint)
self.view.addConstraint(heightConstraint)
Set the constraints in storyboard.
Click on the text field then click on in the bottom left. From there you can choose constraints like that.
To use Auto Layout, you need to define constraints for your text field.Here, I have created four constraints(Leading, Trailing, Top and Height) related to its superview.
func addLabelConstraints(superView:UIView) {
let leading = NSLayoutConstraint(item: searchTextField, attribute: .Leading, relatedBy: .Equal, toItem: superView, attribute: .Leading, multiplier: 1, constant: 15)
superview!.addConstraint(leading)
let trailing = NSLayoutConstraint(item: searchTextField, attribute: .Trailing, relatedBy: .Equal, toItem: superView, attribute: .Trailing, multiplier: 1, constant: 15)
superView.addConstraint(trailing)
let top = NSLayoutConstraint(item: searchTextField, attribute: .Top, relatedBy: .Equal, toItem: superView, attribute: .Top, multiplier: 1, constant: 0)
superView.addConstraint(top)
let height = NSLayoutConstraint(item: searchTextField, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .Height, multiplier: 0, constant: 50)
superView.addConstraint(height)
}
I have these views, both are the same, i want to add them programmatically so i want to add constraints programmatically, i've managed to do same using storyboard but i want to use code for this.
i want to add margins to these views so that first one is at the top, next one is below the first one and so,
i've wrote code like this:
self.view.addConstraint(
NSLayoutConstraint(
item: secondView,
attribute: .Top,
relatedBy: .Equal,
toItem: firstView,
attribute: .Top,
multiplier: 1.0,
constant: 0
))
first view has the constraint in which toItem is current view controller and it works, but the second view does not work this way, it just draws it on top of the first view, i want it to be below it, only way i can do this is in constant: 0 enter the height of the view, which i don't like
any suggestions?
The code you supplied is 99% right but
self.view.addConstraint(
NSLayoutConstraint(
item: secondView,
attribute: .Top,
relatedBy: .Equal,
toItem: firstView,
attribute: .Top,
multiplier: 1.0,
constant: 0
))
Your attaching the top of secondView to the top of firstView so they would be on top instead you want the top of secondView to the bottom of firstView.
self.view.addConstraint(
NSLayoutConstraint(
item: secondView,
attribute: .Top,
relatedBy: .Equal,
toItem: firstView,
attribute: .Bottom, <----------
multiplier: 1.0,
constant: 0
))
The constant is the distance.