UIView position does not respect NSLayoutConstraint when altered from viewDidLoad - ios

I have two views, one visible (grey) and one hidden (red). The visible view is constraint to the superview top with a constant of 32, and the other constraint to the superview top with a constant of 16.
The top of the visible view is also constrained to the bottom of the hidden view, with a constant of 8. This constraint has a priority of 749, because it is initially unsatisfiable.
The idea is that the visible (grey) view should be 32 points below the superview normally, but when the hidden (red) view is made visible, the original (grey) view should be 8 points below the other view (red).
I'm achieving this by keeping a strong reference to the 32-point constraint, and making it active/inactive as I'm hiding/unhiding the view (red).
Here's a picture of my layout:
This works very well normally, but I've been trying to set constraint.active = NO and redView.hidden = NO in viewDidLoad, as I need the textfield to display an error if a certain condition has not been met. For some reason, this does not work. The red view is displayed as expected, but the second view (grey) does not move down as it should (it does not respect the 749 constraint, even if the main constraint is no longer active). Here's a picture of the result:
However, I made the code to inactivate the constraint and display the view run after a small delay (using dispatch_after();), and then it suddenly works as expected:
My question is: why doesn't the view respect the constraint and move down when it is run immediately from viewDidLoad? Why does it suddenly work after a small delay? Is this a decent solution of achieving my goal, given I can get it to work properly?

call it in viewDidLayoutSubviews methode not in viewdidload.
autolayout it take some time to load so call it in viewDidLayoutSubviews.

While using autolayout - (void)viewDidLoad will return the frame which you gave in storyboard.
You have to use - (void)viewDidLayoutSubviews to update the frame according to constraints.
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
//Replace self.view by your view whose constraints need to update
[self.view layoutIfNeeded];
}

Related

Remove one view's layout constraints but keep its subviews'?

Working on a project in iOS 8 using storyboard and auto layout:
In storyboard, specified constraints for this view and its subviews
In code, in response to touch events, I'm going to change this view's size by setting its frame
To make both 1 and 2 warning free, I'm doing the following when first changing its size with code:
[theView removeConstraints:theView.constraints];
theView.translatesAutoresizingMaskIntoConstraints = YES;
theView.frame = CGRectMake(0,0,width,height);
If not doing the first line, Xcode will complain a whole bunch about constraint conflicts, however adding this line will remove all its subview's constraints as well. So my question is: is there way to just remove this uiview's constraints but not its subview, say a button on it still wants to center its self relative to this view's size and position?
First, you don't need to resize the view by setting frame otherwise what is the point of keeping the constraint at first place. You could have position that by simply having a correct initial frame.
Secondly, you have a mis-conception about "TO-WHOM" a constraint has been applied to.
say a button on it still wants to center its self relative to this view's size and position?
When you apply a position related constraint to a view you normally apply it to it's superview. Means if you want to position a subview in the horizontal centre of a view then the constraint is added on view not on the subview. That's why when you called a removeContraints: message on view that position constraint was removed and now your subview isn't bound to any constraint. However the width and height constraints are applied to subviews itself.
To solve this you need to make IBOutlets for constraint that you need to modify, which in your case should be width, height, horizontal x and top constraint; and then change the constant values for them respectively.
theView.widthConstraint.constant = newValue;

add a UIView and recalculate constraints with autolayout

I need your help to make this:
I have a view which width is equal to the width of screen. In this view I have 2 subviews with width 1/2 of parent view and aspect 1:1, so the height of the parent view should be equal to the height of subviews.
After that I need to perform animation which should add to the parent view another view and transform a whole screen like this:
Should I use autolayout programmatically to achieve this? How to get this behaviour easier?
You can...
First, instead of making them half the parent view, I would:
Set the left border of the left view to the left border of the parent view.
Set the right border of the right view to the right border of the parent view.
Set the horizontal space between the left and right views = 0.
Set an equal widths constraint between the left and right views.
Then when you add the new view,
Remove the right border constraint of the right view.
Set the right border of the new view to the right border of the parent view.
Set the horizontal space between the right view and new view = 0.
Set an equal widths constraint between the right and new views.
Then call
[UIView animateWithDuration:0.5 animations:^{
[view layoutIfNeeded];
}];
Note that if the default is just two views, you could add the first set of constraints via storyboard (which is easier than doing it programmatically). The rest of it you can do programmatically: See Apple documentation.
Personally, I like this method, because you don't have to do any math on the size or positions of the views - autolayout will figure it out for you.
The easiest way would be.
In design time(Storyboard) add 3 views inside the parent view.
For all the child views (View 1,2 and 3) create a width constraint and create outlets for the same. When design is completed for all the views, set 0 as constant for the third view's width constraint.
Implement "viewDidLayoutSubviews" method for the view controller as below.
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
}
Inside this method, find the current width of the parent view, and store it in a variable "X".
When the view is loaded for first time, set the width constraint's constant as x/2 for first and second view.
When 3rd view is ready to display, set width constraint's constant as x/3 for all the three view inside an animation block.

navigation bar possibly causing UIView offset

I have a view controller that consists of two UIViews. One view is the super view and the other is a subview. Both views are the same size 320X568 and have the same origin (0,0). I added constraints to the subview by first setting an aspect ratio to itself, then an equal width ratio to the superview with a multiplier of 1. I then centered the subview horizontally and vertically in the container. When an up drag gesture is recognized, the subview is suppose to move up a certain amount using CGRectOffset. I noticed that the view wasn't ending in the right position, so I removed all the offset code to see what was going on. In viewDidLoad I check the origin.y of both the super view and subview and its 0 - which is correct. However, once the up drag gesture is recognized, I do another NSLog for the y origins and the subview prints -31.5 and the superview prints 64. Not sure how this is happening with all the CGRectOffset commented out. I'm thinking this might have to do with the navigation bar but I'm not sure.
I don't know what the deal is with the CGRectOffset, but I do know what the "problem" is without it. There's no problem! The only thing here is that you are checking the origin.y of the views too soon, namely in viewDidLoad.
Remember, in viewDidLoad nothing has actually happened yet. The view controller has a view but that's all. In particular, the view is not yet in the interface and has not been given its layout yet.
So it's really just a matter of knowing what the events mean. You're assuming that viewDidLoad means the view is in the interface and has its position and size. It doesn't mean anything like that. If you want to know what the view's position is after layout, you need to wait until after layout. Like, viewDidLayoutSubviews is a good place. It means that layout has just occurred!
As for the 64 value, it's just what you would expect. You have pinned your view's top to the top layout guide - which means the bottom of the navigation bar, exactly as you have suggested.

Self sizing Cells,. Autolayout and hidden UIViews

i am using Autolayout to set automatically the height of a cell. But i want to use some hidden UIViews in my Cells. So in some rows they should be hidden. The problem here is, if the label is still hidden it gets recognized by autolayout and it uses the space.
So i guess i need to update the height of my label, or the height of my Constraints.
What is the correct way if i am using Autolayout to hide UIElements in a Cell?
How can i update an existing (from Storyboard) Constraint?
Edit: Found out that i am able to update Constraints with Storyboard, is that the correct way to handle that? - but which is the correct one to setup the height? (This is just a Demo Project)
This is standard task with autolayout. Currently, there are two ways of solving this task:
Removing hidden views
Changing constraints priority
Removing of hidden views
Looks pretty easy and you won't have any problems if you don't use UITableView (you have to put it back in prepareForReuse), UICollectionView or your data can appear while you're on screen, where you've just removed views.
Removing of views is "heavy" operation, so you need to think twice, if you pick this way.
In CustomCell.m:
- (void) prepareForReuse {
[super prepareForReuse];
// add removed views again
// establish constraints
}
- (void) configureCell {
// check if you need to hide a view
[view removeFromSuperview];
}
Changing constraints priority
Assume, you have following cell:
View A
View B
If B sometimes can be hidden, then specify for View A following constraints:
Space to B with 750
Bottom Space to superview with 500
If you set B as hidden, change space to B = 500, bottom space to superview = 750.
Undo that change in prepareForReuse

iOS autolayout-move a view located inside a tableviewcell to the center of the screen

I have a tableview with cells containing text views as well as imageviews. My project is currently using AutoLayout. My goal is to get the imageview to display in fullscreen when it is tapped. One option is to use a modal view controller, but I want to have this work sort of like the way tapping on images in the facebook app works, the app centers the image and fades the background.
Since I'm using autolayout, I cannot simply set the frame of the imageview to fill the screen. Instead, I need to use autolayout constraints. My image view has 5 constraints, a constraint setting a distance from the bottom of the cell, as well as the left an right sides, and one controlling the image height. The last is a vertical space constraint between the textview above the image view and the top of the image. While this would appear to conflict with the height and bottom constraints, for some reason interface builder forces me to have this. To avoid problems, I set this constraint's priority to be less than 1000 (the image should never overlap the textview anyways, since the tableview cell height is set so everything will fit perfectly).
To center the image, I set the distance from the left and right to be zero and remove the vertical space constraint. In order to center the image, I replace the bottom space constraint with a center y alignment constraint to the UIWindow as opposed to the tableviewcell. I want to have it be in the center of the screen, not the cell.
To get the main window I use this:
AppDelegate* myDelegate = (((AppDelegate*) [UIApplication sharedApplication].delegate));
//access main window using myDelegate.window
Then, to set the constraint:
//currently sets the distance from the bottom of the cell to 14
//changing it...
[cellselected removeConstraint:cellselected.imagebottomspace];
cellselected.imagebottomspace = [NSLayoutConstraint constraintWithItem:cellselected.viewimage attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:myDelegate.window attribute:NSLayoutAttributeCenterY multiplier:0 constant:0];
[cellselected addConstraint:cellselected.imagebottomspace];
However, this doesn't work. The changes in the width and height of the image view apply just fine. However, when readding the imagebottomspace constraint, I get an unsatisfiable layout--apparently the constraint conflicts with another constraint which sets the distance between the bottom and the image view to 14, the very constraint I just removed. So it seems that it isn't actually removing the constraint.
When I proceed and let the app break a constraint, the imageview moves, but to the wrong place. It isn't centering in the screen. It moves way up and off the screen.
Obviously what I'm doing isn't right. What am I doing wrong?
So I guess you want something like this:
First, you need to know that as of Xcode 4.6.3, the nib editor (“Interface Builder”) has a bug when setting up constraints in a table view cell. It should create the constraints between the subviews and the cell's content view, but instead it creates the constraints between the subviews and the cell itself. This tends to screw up layout at runtime. (This bug is fixed in Xcode 5 and later.)
The consequence of this is that you should either remove all of the constraints that were in the nib and recreate them in code, or just get rid of the nib and create the cell's entire view hierarchy in code.
Second, there's an easier way to do the image zooming. Here's the basic procedure when a cell is selected:
Convert the selected cell's image view bounds to a CGRect in the top-level view's coordinate system.
Create a new image view just for zooming and set its frame to that CGRect. Set its userInteractionEnabled to YES. Set its autoresizingMask to flexible width and height. Add a tap gesture recognizer.
Add the new image view as a subview of the top-level view.
Set the cell's image view's hidden property to YES.
In an animation block, set the new image view's frame to the top-level view's bounds.
Disable the table view's panGestureRecognizer.
When the new image view is tapped, reverse the procedure:
Convert the selected cell's image view bounds to a CGRect in the top-level view's coordinate system.
In an animation block, set the zoomed image view's frame to that CGRect.
In the animation completion block:
Remove the zoomed image view from its superview.
Set the cell's image view's hidden property to NO.
Enable the table view's panGestureRecognizer.
Since you're not moving the original image view, you don't have to mess with its constraints. Hidden views still participate in layout.
Since you're creating the new image view in code, it will have translatesAutoresizingMaskIntoConstraints set to YES by default. This means that you can just set its frame. Auto layout will automatically turn the frame into constraints.
You can find the full source code in this github repository.
I've just come across a similar issue. I think that the reason for these problems are that the views embedded in UIScrollViews exist in a different bounds system to those of the views outside it. This is effectively how scrolling works in the first place, think of it as just applying a variable offset to the views it contains. Autolayout doesn't know how to translate between these different coordinate systems so any constraints that bridge across aren't going to be applied the way you expect.
To quote from Erica Sadun's excellent book iOS Auto Layout Demystified (from the section 'Constraints, Hierarchies, and Bounds Systems'):
"Be aware of bounds systems. You should not relate a button on some
view, for example, with a text field inside a separate collection
view. If there's some sort of content view with its own bounds system
(such as collection views, scroll views, and table views), don’t hop
out of that to an entirely different bounds system in another view."

Resources