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)
Related
I have a subView named loginView and within that a few other elements, namely loginButton and loginUsername
I am trying to programatically add a Facebook login button that has constraints relative to loginButton, loginView and loginUsername
Here is my code
self.loginView.addSubview(facebookLoginButton)
let facebookLoginButtonTopConstraint = NSLayoutConstraint(
item: facebookLoginButton,
attribute: NSLayoutAttribute.Top,
relatedBy: NSLayoutRelation.Equal,
toItem: loginButton,
attribute: NSLayoutAttribute.Bottom,
multiplier: 1,
constant: 8
)
let facebookLoginButtonLeadingConstraint = NSLayoutConstraint(
item: facebookLoginButton,
attribute: NSLayoutAttribute.Leading,
relatedBy: NSLayoutRelation.Equal,
toItem: loginView,
attribute: NSLayoutAttribute.Leading,
multiplier: 1,
constant: 8
)
let facebookLoginButtonWidthConstraint = NSLayoutConstraint(
item: facebookLoginButton,
attribute: NSLayoutAttribute.Width,
relatedBy: NSLayoutRelation.Equal,
toItem: loginUsername,
attribute: NSLayoutAttribute.Width,
multiplier: 1,
constant: 0
)
self.loginView.addConstraints([
facebookLoginButtonTopConstraint,
facebookLoginButtonLeadingConstraint
])
self.facebookLoginButton.addConstraint(facebookLoginButtonWidthConstraint)
This code is going in my viewDidLoad method. The error I'm getting is:
The view hierarchy is not prepared for the constraint:
When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] to debug.
Any help greatly appreciated, thanks!
The error says all. You need to prepare all views to hierarchy before adding constraints. Check that all four views are added to view hierarchy before adding constraints (didn't you forget addSubvew for some view?). Do not move this code to viewWillLayoutSubviews method, this is wrong hint! Because you don't want to add constraints multiple times. Also, you should add facebookLoginButtonWidthConstraint to view that owns both (loginView?), facebookLoginButton and loginUsername, since this is shared constraint
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?
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.
I have a view divided into four sections. A corner view in the top left (unused, invisible); a horizontal view across the top (timelineAreaView in the code) displaying one set of axis labels; a vertical view on the left side displaying another set of axis labels, and the rest of the view is a large collection view. It's not unlike a spreadsheet.
The problem is that I am trying to use a UIScrollView in the top view. If I add a test UIView to the top everything is fine. Here's the code:
let timelineViewController = self.storyboard?.instantiateViewControllerWithIdentifier("TimelineViewController") as? TimelineViewController
addChildViewController(timelineViewController!)
timelineAreaView.addSubview(timelineViewController!.view)
timelineViewController!.didMoveToParentViewController(self)
timelineViewController!.view.translatesAutoresizingMaskIntoConstraints = false
timelineAreaView.addConstraint(NSLayoutConstraint(item: timelineViewController!.view, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal,
toItem: timelineAreaView, attribute: NSLayoutAttribute.Left, multiplier: 1.0, constant: 0))
timelineAreaView.addConstraint(NSLayoutConstraint(item: timelineViewController!.view, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal,
toItem: timelineAreaView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0))
timelineAreaView.addConstraint(NSLayoutConstraint(item: timelineViewController!.view, attribute: NSLayoutAttribute.Right, relatedBy: NSLayoutRelation.Equal,
toItem: timelineAreaView, attribute: NSLayoutAttribute.Right, multiplier: 1.0, constant: 0))
timelineAreaView.addConstraint(NSLayoutConstraint(item: timelineViewController!.view, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal,
toItem: timelineAreaView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: 0))
TimelineViewController is a plain UIViewController with the UIView property backgroundColor set to blue.
Here's the result, displayed in that excellent tool Reveal:
So far so good. But it needs to have a UIScrollView. So If I add a UIScrollView to that UIViewController in the storyboard it looks like this:
Its constraints now look like this:
Here's the problem shown in Reveal - the UIScrollView is highlighted:
Reveal Layout Inspector shows:
So WHY are there 20 pixels added to the y origin and subtracted from the x origin in the frame?
My first thought was to look at the view controllers settings for "Adjust Scroll View Insets" and "Under Top Bars" but these have no effect. I cannot find any setting that has any effect here.
Going back to the storyboard and compensating for the two mysterious 20 pixel errors, i.e.
This makes it look just like I want. But I shouldn't have to do this. What's going on?
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