Autolayout vs frames - ios

Let's say that i have a view loaded from a nib that has layout constraints changing its appearance depending on its size.
I load the view from the nib and get a reference to UIView.
I then manually change view's frame (e.g make it really big or small) and add it as a subview to already visible view.
Will the constraint system automatically figure that frame change should trigger auto layout recompilation? How does auto layout mix with manual frame/bounds changes? Where can i get detailed documentation on this?

No, if your want the autolayout to re-apply you need to call [view layoutIfNeeded], and if you set frames before the layout finished to apply, it will be override. My advice is to modify constraints instead of setting frames.

Related

Set AutoLayout width constraint on top-level UIView of custom XIB from Interface Builder or code?

How do you set a width constraint on the top-level view of a custom XIB?
This would be Content View in the image below.
Clicking the Add New Constraints button doesn't let you specify a width. The option is disabled (along with other options).
Is this possible from Interface Builder? If not, is this possible from code? If not, that might explain some layout issues ...
Auto-layout sets the .translatesAutoresizingMaskIntoConstraints to true on the top-level view loaded from a custom XIB.
You want to set the .translatesAutoresizingMaskIntoConstraints to false via code when you load and use that view.
Is this possible from Interface Builder?
No. If you have a fixed size to give the view, then set its Size metric to Freeform and give it that size, directly.
If not, is this possible from code?
Yes, of course. You must have, somewhere, some code that loads the nib, obtains the view, and puts the view into the interface. At that point it is up to you to size and position it, and you can certainly use autolayout to do so, though no law requires that you do it that way; you could just give it a frame and leave things at that, or if the size coming from the nib is right, then you can leave it alone and just position the view with its frame origin or center.

How to adjust the parent view's height according to its subviews using auto layout

I have cutomized a view, whose subviews's height will dynamically change(I'm changing the height by changing the constraint).
My intention is to let the view's height adjust to its subviews' height, so I won't care for anything, and that's what autolayout is for.
In practise, there is no way to add any constraints to the root view in IB.
And I found I can still change the frame of the root view, which appears to be able to solve my current problem. But I don't think it's recommended to compound auto layouts and frames.
So, any other suggestions?
It's perfectly fine to have some views using the old system of layout (i.e., autoresizing and frame setting) while other views are using Auto Layout directly. The important thing to remember is to set the translatesAutoresizingMaskIntoConstraints property to NO on views instantiated in code that are using Auto Layout.
However, you can't use Auto Layout on a view controller's root view.
Using Auto Layout, subviews can dictate the size of their superview–but this won't work if the superview is using autoresizing and frame setting.
I don't know if the root view you're working with is the root view of a scene's main view controller or the root view of a child view controller. If the former, I don't recommend adjusting the root view's frame in iOS 7. Just let the root view fill the whole screen (which is its default behavior).
Subviews have their size dictated by their superview, not the other way around. For your special case, I would make the subview delegate its height changes back to the superview. While you could then adjust the rect of the superview (and avoid changing the subview's height altogether), I would do this:
Create a subview of your view controller that behaves as your new resizing parent. The viewController.view can then remain fullscreen. Your view hierarchy would then be viewController.view->Resizing Parent->Resizing Child.
Create an IBOutlet to the constraint you want to modify.
Create a protocol for the child view to send resize callbacks to its parent.
Update the parent's constraint constant when it receives the callback.
You shouldn't have to modify the height of the child view, because the constraints will do that for you.

iOS AutoLayout - when does it Run?

I have a XIB file where I specify constraints at design time.
In addition to controls with constraints, I also have controls that have no constraints in the XIB that I want to position programmatically at run time.
I do this by repositioning views manually in didRotateFromInterfaceOrientation.
When I rotate the device, my code places the controls that are manually controlled but then later it appears that auto layout messes up the placement of manual controls that have no constraints on them in the Interface builder.
Question - When exactly does Auto layout run if I rotate the device.
Auto Layout runs at the end of the run loop after setNeedsLayout is called on a view, or immediately when setNeedsLayout is followed by layoutIfNeeded. Note that there are many methods that may call setNeedsLayout in their implementation, such as addSubview:, removeFromSuperview, etc.
A couple suggestions:
Instead of updating your constraints in didRotateFromInterfaceOrientation:, try doing so in updateViewConstraints.
You may also want to add placeholder constraints in Interface Builder to the views you will position programmatically. I think IB will automatically constrain views to their x/y position if a placeholder constraint is not set, but I'm not 100% sure on that.
The important thing to know is that the results of constraint calculations are applied in layoutSubviews. So if you want to set frames, you do so after calling super in layoutSubviews or in your view controller's viewDidLayoutSubviews.
Having said that, I've only experimented with this. I've not done it in production code and can't say that you won't run into issues.
I'm actually not clear on whether it's OK to set frames manually like this. The only relevant bit of information I've come across is the following from Apple's Auto Layout documentation:
You cannot set a constraint to cross the view hierarchy if the
hierarchy includes a view that sets the frames of subviews manually in
a custom implementation for the layoutSubviews method on UIView (or
the layout method on NSView).
Here, the phrase "view that sets the frames of subviews manually" implies to me that manually setting frames is OK. I'd be interested to know if anyone has a see a more explicit discussion on this.
you must not change the frame you must connect the constraint with iboulet and in the didRotateFromInterfaceOrientation you can change the constraint like this:
(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
if (UIInterfaceOrientationIsLandscape(fromInterfaceOrientation)) {
self.constraintTop.constant = 50;
}else{
self.constraintTop.constant = 100;
}
}

How do you override a storyboard element/view on iOS?

I have a UITextView set in my storyboard at a certain position. When the app first loads up, I would like the textView to be at a different position then where it is in the storyboard. I do this programmatically by setting its frame to a different frame with a different origin (later the textView will be animated back to the original position). But no matter how I do it, the app always starts up with the textView in its storyboard assigned position. I can't seem to do this anywhere... viewDidLoad:, viewWillAppear:, nothing works. How can I override it?
One other thing... lets say the textView has a different origin then it does in its storyboard (the frame was reset at some point in the app). If I add a subview to the textview using addSubView:, the textView resets to its original position in the storyboard. Why?! Any way to stop this?
If you want to change the position of the textView when it starts up, I'd suggest that you do not have it in the storyboard and instead add it to the subview with its correct frame.
Especially with Autolayout in effect, having it there already means you would have to modify its constraints and if you're modifying its position programmatically already anyway, you might as well add it to the view programmatically too where you have more control over its constraints programmatically as well.
Under auto layout you can't set frames - well, you can, but as soon as another layout pass takes place, the frame will be reset to that defined by the constraints.
The simplest way to change the size or position of views you've laid out in interface builder using auto layout is to create outlets to the various constraints, and then edit their constant properties. If that doesn't give you enough flexibility, you still need outlets to the constraints, but when you want to move things you'll have to delete those constraints and add new ones.

Constraints changed when modifying UIViewController.frame

I have a UIViewController which I add to another UIViewController as a child. When the child gets added, the parent set frame on my child using childVC.frame = ...., but this fubars my constraints.
I can duplicate this issue in IB easily. Create a UIViewController, change the size to be "Freeform" and ditch the status bar (just so it looks right visually). Add in a UIButton (or anything) and constraints so so they are both the same size (see image below)
Now change the size of the top level view in the view controller and watch what happens.
It changed my constraints on me. I wanted my button to just get bigger and still hold the original constraints. Something which happens automatically if you toggle between iPhone retina 3.5/4.0 in screen or change from landscape to portrait. But why not when I resize the view? This is making a royal pain to lay things out in freeform and test different sizes.
More importantly... why does the exact same thing happen when I set the frame in code when I add the child for my VC and how do I prevent it?
Frame based calculations and Auto Layout are not compatible. Or at least, they mean stormy waters. That's why you are having issues. Having switched to Auto Layout you have to accept that you no longer manipulate frame directly.
So, you cannot do childVC.frame = whatever.
Instead, you must now use Auto Layout to update the size of childVC.frame. Do that by creating - in IB - an IBOutlet that is the width constraint of childVC. Similarly, create another IBOutlet that is the childVC height constraint.
In your view controller, update the constant value to change the size.
myWidthConstraint.constant = 123.f;
The place to do this is in the controller's updateViewConstraints method, or in the case of a view, updateConstraints.
That said, it's better to set up Auto Layout so that you don't need any programmatic updates. When properly defined, it will know how to handle things with the view's bounds change. To be honest, it should only be necessary to update sizes programmatically if you want to do something more unusual.
Can I just say however that you are better

Resources