I have set the following constraint programmatically on a UISwitch that was set as an IBOutlet:
func setConstraints {
let leadingConstraintTimeSwitch1 = NSLayoutConstraint(item: timeSwitch, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: timeLabel, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: 10)
view.addConstraint(leadingConstraintTimeSwitch1)
Whenever I change the constant of this constraint, the X position of the switch remains the same. However, when I go to the Size Inspector of the switch and change its X value, the switch's position does update correctly, however I do not want to use the Size Inspector. How can I get the constraint set programmatically to override the X value in the Size Inspector? I have also added a constraint specifying the Y position of the switch (a bottom constraint), and this one does work. Thanks!
There is not alot of information to go from in your description..
I normally add my constraints like so:
NSLayoutConstraint(item: timeSwitch, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: timeLabel, attribute: NSLayoutAttribute.Trailing, multiplier: 1, constant: 10).active = true
are timeSwitch and timeLabel part of the view hierarchy?
have you set translatesAutoresizingMaskIntoConstraints to false?
Are there any other constraints at play that could be affecting it?
Related
I'm creating a custom keyboard in Xcode with swift. Everything runs great but I am running into a problem with constraints. Ill explain what I've done and what I am looking to do.
what I have done:1)I have created a 'world' button that will switch between the iOS default keyboard and the custom keyboard. It is constrained to the bottom left of the view, no matter what device it is loaded onto (iPhone 5,6,7 iPad etc). 2)I have then created a collection view that is constrained to start at the edge of the world button no matter the device. 3)I have created a delete button that is constrained to the bottom right of the view, no matter the device.
what I want to do: 1)I want the collection view to start at the world button and end at the delete button, no matter the device.
The trouble I am having is that the delete button overlaps the collection view on smaller devices. I want the collection view to stop at the delete button but cannot figure out why my constraints are not working.
These are the relevant constraints for the collection view.
// create the constraints
// leading constraint
let categoriesCollectionViewLeadingConstraint = NSLayoutConstraint(item: categoriesCollectionView, attribute: NSLayoutAttribute.leading, relatedBy: NSLayoutRelation.equal, toItem: backButton, attribute: NSLayoutAttribute.trailing, multiplier: 1, constant: 0)
// add the leading constraint
view.addConstraint(categoriesCollectionViewLeadingConstraint)
// bottom constraint
let categoriesCollectionViewBottomConstraint = NSLayoutConstraint(item: categoriesCollectionView, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: 0)
// add the bottom constraint
view.addConstraint(categoriesCollectionViewBottomConstraint)
// trailing constraint
let categoriesCollectionViewTrailingConstraint = NSLayoutConstraint(item: categoriesCollectionView, attribute: NSLayoutAttribute.trailing, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.trailing, multiplier: 1, constant: 0)
// set the priority to less than 1000 so it works correctly
categoriesCollectionViewTrailingConstraint.priority = 999
// add the trailing constraint
view.addConstraint(categoriesCollectionViewTrailingConstraint)
I think you should constraint your collection view like this:
// trailing constraint
let categoriesCollectionViewTrailingConstraint = NSLayoutConstraint(item: categoriesCollectionView, attribute: NSLayoutAttribute.trailing, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.trailing, multiplier: 1, constant: - deleteButtonWidth)
I have a controller where I add a subview programmatically. With the configuration of the subview I add autolayout constraints programmatically. Everthing is working except that the view doesn't react on touches if I add the constraints and even the set backgroundcolor is not displayed.
The buttonView should be displayed in the lower right corner of my parent view.
Any ideas what could be wrong?
Here is how I add my constraints:
private func configureAutolayoutConstraints(buttonView: UIView, parentView: UIView){
buttonView.translatesAutoresizingMaskIntoConstraints = false
let bottomConstraint = NSLayoutConstraint(item: buttonView, attribute:
.Bottom, relatedBy: .Equal, toItem: parentView, attribute: .Bottom, multiplier: 1.0, constant: -130)
parentView.addConstraint(bottomConstraint)
let trailingConstraint = NSLayoutConstraint(item: buttonView, attribute:
.Trailing, relatedBy: .Equal, toItem: parentView, attribute: .Trailing, multiplier: 1.0, constant: -90)
parentView.addConstraint(trailingConstraint)
}
Autolayout engine needs at least 4 constraints to determine the frame of view. You have applied bottom and trailing constraints only. You need either width+height OR leading+top constraint to make it work.
I am trying to add constraint for right attribute to a label and a button programmatically but it doesn't work for right side constraint.
Button and label are on both the same UITableView and same cell. They both have simple width, height, x and y position constraints set in storyboard. I need to change the x position in code.
Following left constraint is working OK, I changed the value and it's reflected in simulator.
dateLabelLeft = NSLayoutConstraint(item: dateLabel,
attribute: .Left,
relatedBy: .Equal,
toItem: self.view,
attribute: .Left,
multiplier: 1,
constant: 50
I just changed the .Left with .Right and even tried .Trailing but it doesn't work. I have double checked the storyboard and there is no red line for the Button.
buyButtonRight = NSLayoutConstraint(item: dateLabel,
attribute: .Right,
relatedBy: .Equal,
toItem: self.view,
attribute: .Right,
multiplier: 1,
constant: 50
edit :
constraints are added using view.addConstraint
I think you dont need to add constraints programmatically if you have added from storyboard....alternatively you can create the IBOutlet of specific constraints and can change the constant value of constraint you mapped programmatically.
You realize that creating an NSLayoutConstraint does nothing? You have to add it to a view as well.
I am trying to put an UIImageView on the upper center of screen horizontally which is coding in the viewDidLoad( ). And I have two pre-plans, which means I do not know the specify functions or APIs.
1). I want to set a variable which equal to the half of screen's width. And I know it gonna work with something about 'bounds' or 'frame'. Pathetically, I do not know those specify function or parameters.
2). In order to make sure the resolution, I want to figure out the currentDevice. After that, I can set UIImageView with CGRectMake((resolution.x)/2, y, length, width). Is there any function can confirm the currentDevice?
And I think the first one is more efficient than the second one.
A big appreciate for your help and guidance.
Ethan Joe
I would suggest to use autolayout:
var imageView = UIImageView()
imageView.setTranslatesAutoresizingMaskIntoConstraints(false)
view.addSubview(imageView)
// Create the constraints and add them to a Array
var constraints = [AnyObject]()
// This constraint centers the imageView Horizontally in the screen
constraints.append(NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.CenterX, multiplier: 1.0, constant: 0.0))
// Now we need to put the imageView at the top margin of the view
constraints.append(NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: view, attribute: NSLayoutAttribute.TopMargin, multiplier: 1.0, constant: 0.0))
// You should also set some constraint about the height of the imageView
// or attach it to some item placed right under it in the view such as the
// BottomMargin of the parent view or another object's Top attribute.
// As example, I set the height to 500.
constraints.append(NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 500.0))
// The next line is to activate the constraints saved in the array
NSLayoutConstraint.activateConstraints(constraints)
This code horizontally centers the imageView in every device. I suggest you investigating AutoLayout, it's a nice feature.
Here's a nice link where you can start with it:
Learning to love Auto Layout... Programmatically
So, the best solution to this would be to utilize auto layout.
Say your UIImageVew is defined as var imageView = UIImageView!.
Then you would do the following.
var centerXConst = NSLayoutConstraint(item: imageView, attribute: .CenterX, relatedBy: .Equal, toItem: self.view, attribute: .CenterX, multiplier: 1, constant: 1)
var centerYConst = NSLayoutConstraint(item: imageView, attribute: .CenterY, relatedBy: .Equal, toItem: self.view, attribute: .CenterY, multiplier: 1, constant: 1)
self.view.addLayoutConstraints([centerXConst, centerYConst])
What you are doing is setting the center of the imageView on the x and y axis respectively to the same as the center of the view on the x and y axis respectively.
To find the width of the view as you proposed in 1), do the following:
var screenWidth = self.view.bounds.width
I have programmatically created an auto layout constraint that moves a label up 25 points from the bottom of its parent view. There are no other constraints I define on this scene. 25 is fine on most devices, but it's too far up on the iPhone 4s. I would like to make it about 15 up from the bottom for that screen size. But then I was thinking it would be much better to obtain a variable constant instead of enforcing a fixed value for all screen sizes, or attempting to change it for one specific screen size. But it doesn't seem this is possible with Auto Layout. Is it?
I was thinking one way to obtain a variable constant would be to calculate a value based on the screen (or view) height. For example, the constant would be equal to self.view.frame.size.height / 30. On iPhone 4 (portrait only) it would be 16, iPhone 5 it would be almost 19, etc. This would be perfect. Or another way, 3% of the height. These values would need to be dynamic for rotations though, as the height will change.
Is it possible to use auto layout constraints that have variable constants based on some calculation or screen/view height? Or is there some other solution that will allow me to obtain the desired behavior - avoiding fixed auto layout constants?
Here's some example code, from viewDidLoad:
let footerLabel = UILabel()
footerLabel.text = "my text"
footerLabel.sizeToFit()
footerLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(footerLabel)
self.view.addConstraint(NSLayoutConstraint(item: footerLabel, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1, constant: -25))
self.view.addConstraint(NSLayoutConstraint(item: footerLabel, attribute: .CenterX, relatedBy: .Equal, toItem: self.view, attribute: .CenterX, multiplier: 1, constant: 0))
I was thinking one way to obtain a variable constant would be to
calculate a value based on the screen (or view) height. For example,
the constant would be equal to self.view.frame.size.height / 30. On
iPhone 4 (portrait only) it would be 16, iPhone 5 it would be almost
19, etc. This would be perfect. Or another way, 3% of the height.
These values would need to be dynamic for rotations though, as the
height will change.
You can just change your first constraint to:
self.view.addConstraint(NSLayoutConstraint(item: footerLabel, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 0.97, constant: 1))
Keeping the constant 0 and changing the multiplier to 0.97 means the bottom of your label will always be 3% higher than the bottom of your view.
You can setup "spacer" view with which height are constrained to 3% of the view.
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
let spacer = UIView()
label.setTranslatesAutoresizingMaskIntoConstraints(false)
spacer.setTranslatesAutoresizingMaskIntoConstraints(false)
spacer.hidden = true
view.addSubview(spacer)
view.addSubview(label)
var views = ["spacer":spacer, "label":label]
view.addConstraint(NSLayoutConstraint(item: spacer, attribute: .Height, relatedBy: .Equal, toItem: view, attribute: .Height, multiplier: 0.03, constant: 0))
view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[label][spacer]|", options: nil, metrics: nil, views: views))
view.addConstraint(NSLayoutConstraint(item: label, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1, constant: 0))
label.backgroundColor = UIColor.purpleColor()
label.text = "THIS IS MY LABEL"
}
I would not assign the constraint directly to the view.
I would:
Create a variable property for your NSLayoutConstraint
In viewDidLoad, use this function to initialize it:
(instancetype)constraintWithItem:(id)view1
attribute:(NSLayoutAttribute)attr1
relatedBy:(NSLayoutRelation)relation
toItem:(id)view2
attribute:(NSLayoutAttribute)attr2
multiplier:(CGFloat)multiplier
constant:(CGFloat)c
Add the constraint to the view using self.view.addConstraint
Assign different values (using .constant) to that constraint when orientation changes or based on device.