No Constraints found in viewDidLayoutSubviews - AutoLayout - ios

Happy Thanksgiving if you celebrate it!
I have a UIImageView on storyboard, setup with 4 constraints. The center x constraint has an identifier set (via storyboard), "imageViewTwoCenterX".
I'm trying to find that constraint with the identifier.
PROBLEM: The code below returns 0 for the constraints array count, and never finds the constraint with the identifier.
Am I doing something wrong? Wrong practice? All help is appreciated!
I use this code:
override func viewDidLayoutSubviews() {
print("Constraints Count: \(imageViewTwo.constraints.count)")
for constraint in imageViewTwo.constraints {
if constraint.identifier == "imageViewTwoCenterX" {
print("Found it!")
}
}
}

For constraints other than width/height constraints, IB adds them to the view's superview. So you won't find them among the image view's constraints. Try listing its superview's constraints instead.

Related

Getting the correct readableContentGuide after rotation

I have a nested collectionView which I would like to get the readableContentGuide for after rotation in order to set the content inset correctly.
This is what it looks like:
I have already tried to subclass the collection view and get the value from layoutMarginsDidChange, traitCollectionDidChange, and layoutSubviews.
However the value I get there is always the previous value (i.e when I'm in portrait I get the landscape value and vice versa)
I have also tried to set the inset in the collectionView's collectionView(_:layout:insetForSectionAt:).
Currently, the only solution that seems to work is observing the bounds of the collection view, but that feels a bit hacky.
Any thoughts on how this can be done?
If you are using Autolayout on storyboard you should activate "Follow Readable Width" option for the superview. First, make sure that the collection view is attached to the superview's margins. Then go to the superview and open Size Inspector, and select the option:
Thanks matt for the answer.
For programmatic Autolayout you don't need margins, just attach the collection view to the readableContentGuide of the superview. Like this:
let cv = collectionView
// Guide of the superview
let readableGuide = view.readableContentGuide
NSLayoutConstraint.activateConstraints([
cv.topAnchor.constraintEqualToAnchor(readableGuide.topAnchor),
cv.bottomAnchor.constraintEqualToAnchor(readableGuide.bottomAnchor),
cv.rightAnchor.constraintEqualToAnchor(readableGuide.rightAnchor),
cv.leftAnchor.constraintEqualToAnchor(readableGuide.leftAnchor)
])
If you prefer frame-based programmatic layout you don't need to use layoutMarginsDidChange, traitCollectionDidChange of the superview, neither observe bounds. The best place for layout code is viewWillLayoutSubviews() func of the controller. This will handle any bounds changes including interface rotations.
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
collectionView.frame = view.readableContentGuide.layoutFrame
collectionView.collectionViewLayout.invalidateLayout()
}
Here I explain why we need to invalidateLayout().

How to get UITableView constraints?

I have a UITableView with constraints. When, I try to retrieve the constraints for a UITableView, it's returning 0 elements. Any help?
self.tableView.constraints
Retrieving constraints programatically is not a very good idea.
You can get all the constraints affecting a view using constraintsAffectingLayout(for:), however, you will probably get much more constraints than you expect.
The best solution is to save references to given constraints when you are adding them. If you are using a storyboard or a xib, create an IBOutlet for them.
You need to get table view superview constraints and check the table view as first or second item.
for constraint in (self.tableView.superview?.constraints)! {
if constraint.firstItem as! NSObject == self.tableView ||
constraint.secondItem as! NSObject == self.tableView {
// table view constraints
}
}
Depends where the constraints are applied.
If you have a UITableView in UIViewController and add constraints, the constraints are applied in UIViewController not in UITableView
Use this code instead:
self.tableView.superview?.constraints

Constraints not updating automatically in ios

I have created a storyboard as in the image. In that I have set the vertical spacing between the red view and tableView is around 20. Its working good.
But when i tried to change the height of red view then the tableview should comes up with the constraints I added but the table view is remained constant at the same place.
I used the following code
redView.frame.size = CGSize(width: 1008, height: 0)
self.view.layoutIfNeeded()
Whenever you are adding a constraint to any object on storyboard then onwards that object's frame will be maintained by autolayout engine. This is the primary design principle I guess. In this case what you can simply do is that just make an outlet of the desired constraint which you want to update and update that constraint within your code. For your understanding I am attaching a screenshot.
Now after making a constraint outlet it must look like as follows.
#IBOutlet weak var containerViewHeight: NSLayoutConstraint!
Now to update the height you just have to do one thing.
containerViewHeight.constant = 0
In my opinion if this update is not working inside viewDidLoad or viewWillAppear then please update inside viewDidLayoutSubviews because when viewDidLoad get called then iOS doesn't apply the layout properly.
In my case what I will do is -
override func viewDidLayoutSubviews() { //This ensures all the layout has been applied properly.
super.viewDidLayoutSubviews()
containerViewHeight.constant = 0
}
Note: - To achieve some nice animation effect during layout changes you can apply this constraint update inside an UIView animation block like as follows. But remember, to see the effect nicely you have to call this after presenting the view properly like after viewDidAppear etc.
UIView.animateWithDuration(1.0, delay: 0.2, usingSpringWithDamping: 8.0, initialSpringVelocity: 1.0, options: UIViewAnimationOptions.CurveEaseIn, animations: {
containerViewHeight.constant = 0
self.view.layoutIfNeeded()
}, completion: nil)
Sorry for any mistake.
Update/Edit is most welcome.
Hope this helped. Thanks
You should be using Autolayout to manipulate the redView's height. Modifying its frame's size is not Autolayout. Modify its height constraint (if it exists)
You need to either use constraints or changes to frames! You cannot do both!
What you need to do is to make an outlet reference to your constraint and change its constant-value instead.
If you placed the code in viewdidload or viewwillappear, you will find issues like this. You need to put the code after all the constraints are loaded.
Also, make an object of the height constraint and name it for example redViewHeight.
and change its value by: redViewHeight.constant = 0
It will work!
Replace the constraints for the views with the following.
Redview
Top constraint to ParentView
Leading Constraint to ParentView
Trailing constraint to ParentView
Height Constraint
TableView
Top constraint to RedView
Leading Constraint to ParentView
Trailing constraint to ParentView
Bottom constraint to ParentView
Now wire an IBOutlet for the HeightConstraint of RedView and modify its constant value. The UITableView will adjust its height as desired.

Auto layout with UIScrollView using .xib

I have enabled auto layout in my .xib but unfortunately, scroll view does not scroll anymore.
Constraints on my view:
Constraints on my scrollView:
Constraints on my contentView:
What I am missing here. Thanks for your help.
Try to set contentSize like
sview.contentSize = CGSizeMake(ScreenWidth, 1000);
The contentsize of the scrollView should be determined by the contentView's needed height automatically. Make sure the subviews of contentView have all vertical constraints so the contentView will adapt in height.
In your case you've set contentView.height == view.height. It should work if you remove this constraint and make sure contentView does expand(height) according to it's subviews.
Add one more constraint of Contentview height >= Current height
This will do the trick
You can refer this. It is a nice tutorial for Autolayout with ScrollView.

iOS 8 - Find constraints for a UIView with identifier

I am trying to change a constraint for an ImageView for iPhone 4S,5,5S in viewDidLoad:
for (NSLayoutConstraint *constraint in logoImage.constraints) {
if ([constraint.identifier isEqualToString:#"logoTopIdentifier"]) {
constraint.constant=10;
}
}
It seems that it's not even iterating in the loop. Is there any other way to get a specific constraint with identifier?
You can connect the constraint in storyboard to your view controller class same as connecting a UI element.
Just find the constraints in your storyboard, make the workspace into split view, and drag the constraint to your corresponding view controller class.
Sometimes if you want to animate the position change, you can update the constraint like:
self.theConstraint?.constant = 100
self.view.setNeedsUpdateConstraints()
UIView.animateWithDuration(0.7) { () -> Void in
self.view.layoutIfNeeded()
}
block.
That is it.
Here is the KVConstraintExtensionsMaster library by which you can access the any constant from a view based on the NSLayoutAttribute. No matter whether that constraint added Programmatically or from Interface Builder.
[self.logoImage accessAppliedConstraintByAttribute:NSLayoutAttributeTop completion:^(NSLayoutConstraint *expectedConstraint){
if (expectedConstraint) {
expectedConstraint.constant = 10;
/* for the animation */
[self.logoImage updateModifyConstraintsWithAnimation:NULL];
}
}];

Resources