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
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 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 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 constraints to a facebook sdk login button.
I have the button inside a scroll view and I am trying to add a top constraint to a label that is also in the scroll view. I am able to successfully add the height constraint with no run time errors but the actual constraint does not seem to be applied to the button.
#IBOutlet weak var orLbl: UILabel!
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
var loginFBButton = FBSDKLoginButton()
loginFBButton.readPermissions = ["public_profile", "email"]
let heightConstraint = NSLayoutConstraint(
item: loginFBButton,
attribute: NSLayoutAttribute.Height,
relatedBy: NSLayoutRelation.Equal,
toItem: nil,
attribute: NSLayoutAttribute.NotAnAttribute,
multiplier: 1,
constant: 41)
let topConstraint = NSLayoutConstraint(
item: loginFBButton,
attribute: NSLayoutAttribute.TopMargin,
relatedBy: NSLayoutRelation.Equal,
toItem: self.orLbl,
attribute: NSLayoutAttribute.BottomMargin,
multiplier: 1,
constant: 31)
let leadingConstraint = NSLayoutConstraint(
item: loginFBButton,
attribute: NSLayoutAttribute.Leading,
relatedBy: NSLayoutRelation.Equal,
toItem: self.registerButton,
attribute: NSLayoutAttribute.Left,
multiplier: 1,
constant: 0)
let trailingConstraint = NSLayoutConstraint(
item: loginFBButton,
attribute: NSLayoutAttribute.Leading,
relatedBy: NSLayoutRelation.Equal,
toItem: self.registerButton,
attribute: NSLayoutAttribute.Right,
multiplier: 1,
constant: 0)
self.scrollView.addSubview(loginFBButton)
//loginFBButton.center = self.scrollView.center
loginFBButton.addConstraints([heightConstraint, topConstraint])
}
Then when I include the addition of the top constraint, I am getting a runtime error:
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.
Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to install constraint on view. Does the constraint reference something from outside the subtree of the view? That's illegal.
Both the label and the facebook button are in my scroll view? I've even printed out orLbl.superview and loginFBButton.superview and I am getting optional uiscrollview for both
There is a new (iOS8, OS 10.10), easier way to activate constraints that doesn't involve figuring out which views to add them to. The constraints already know which views they belong to, so first make sure your views have been added as subViews, then call the class method activateConstraints on NSLayoutConstraint to activate them:
NSLayoutConstraint.activateConstraints([heightConstraint, topConstraint])
When you create UI elements programmatically, you need to tell iOS not to turn its frame into constraints. To do that, just after creating loginFBButton do:
For Swift 1.2:
loginFBButton.setTranslatesAutoresizingMaskIntoConstraints(false)
For Swift 2.0 & 3.0:
loginFBButton.translatesAutoresizingMaskIntoConstraints = false
Finally, you are going to need more constraints. I suggest setting a width constraint for your loginFBButton and adding a constraint to position the button horizontally.
I'm having a problem with updating Constraints.
I add contraint by using this code.
var oldHeight = 92 + ((catAmount-10)*100)
self.mainViewport.addConstraint(NSLayoutConstraint(item: self.mainViewport, attribute: .Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 0, constant: CGFloat(oldHeight)))
This code works 100%, but when user scroll down i need to add more info to ViewController so I need to update constraints to. but I get ant error.
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"",
""
)
I've tried to remove constraint by this code:
self.mainViewport.removeConstraint(NSLayoutConstraint(item: self.mainViewport, attribute: .Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 0, constant: CGFloat(oldHeight)))
but it don't remove the old one so I can't add new or update constraint.
What I'm doing wrong? How I can update constraint continuously when i need?
P.S. I'm using ScrollViewController and a ViewController with info in it.
Code update.
class CategoryViewController: UIViewController, UIScrollViewDelegate {
var CategoriesConstraint : NSLayoutConstraint!
/** **/
var aukstis = 92 + (catAmount*100)
self.mainViewport.setTranslatesAutoresizingMaskIntoConstraints(false)
self.CategoriesConstraint = NSLayoutConstraint(item: self.mainViewport, attribute: .Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 0, constant: CGFloat(aukstis))
if(catAmount == 10){
self.mainViewport.addConstraint(self.CategoriesConstraint)
self.showApp(1,secondJson: 0)
} else {
self.CategoriesConstraint.constant = CGFloat(aukstis)
self.mainViewport.updateConstraints()
self.mainViewport.setNeedsLayout()
self.mainViewport.layoutIfNeeded()
}
You need to retain a reference to the original constraint (returned when you created it) so that you can update it (preferably, or remove it if you need to).
Note that the removal doesn't work in your current code because you are creating a new constraint and trying to remove it, but it isn't actually attached to the view yet.
self.xxxConstraint = NSLayoutConstraint(item: self.mainViewport, attribute: .Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 0, constant: CGFloat(oldHeight))
self.mainViewport.addConstraint(self.xxxConstraint)