Apple doc regarding viewDidLayoutSubviews says:
When the bounds change for a view controller's view, the view
adjusts the positions of its subviews and then the system calls this
method. However, this method being called does not indicate that the
individual layouts of the view's subviews have been adjusted. Each
subview is responsible for adjusting its own layout.
Your view controller can override this method to make changes after the view lays out its subviews. The default implementation of this
method does nothing.
I have view1 which contains view2 means view2 is a subview of view1. I am creating a CALayer for my view2 which I want to be exact size of view2. I am using auto layouts so I want to create the CALayer when auto layout finish its work so I can have correct values to set for CALayer frame.
But in viewDidLayoutSubviews method I can't detect when exactly view2 frame is set by auto layout because Apple doc is also saying that
this method being called does not indicate that the individual
layouts of the view's subviews have been adjusted.
after that Apple doc saying
Your view controller can override this method to make changes after
the view lays out its subviews.
I am so confused at what point auto layout will set view2 frame so I can set my CALayer frame accordingly.
As I mentioned in the comments, you don't have to do anything special if your view is aligned with auto layout. If you want a single layer to adjust its bounds to the view's bounds, the most simple and correct way will be overriding layerClass method of the view - in that case layer bounds will be adjusted within a view automatically. If you need one more layer in addition to the standard one, the best place to update layer's bounds may be setFrame: method override.
As of viewDidLayoutSubviews, it is just an event to inform you, that all subviews of viewcontroller's root view are positioned at the desired places. You are not guaranteed that all the rest subviews of those subviews will also be aligned, since it is a responsibility of the parent view itself. they can be both auto layout positioned and manual layout positioned. This event also does not guarantee that all the the views are displayed as desired, especially if their frame change is animated.
Related
I've got an UIImageView inside a root view of a controller, and I've set it to be 90% of the screen width and given it an aspect ratio as constraints to set the dimensions.
In the code I'm trying to do something with respect to the size of the UIImageView at runtime, however when I get the frame.size.height or frame.size.width of the UIImageView they are clearly wrong and way too small.
At first I was accessing the size in viewDidLoad(), after which I found quite a few posts suggesting to do it either in viewWillLayoutSubviews(), viewDidLayoutSubviews(), or viewWillAppear(). Unfortunately I've tried all of those and none of these contexts seem to provide the right value. I suspect it may have something to do with auto layout but I'm not quite sure how to get around this. Any insight as to why this may be would be appreciated
viewDidLoad is too early. At this time, the views have the frames they were given in the storyboard. Ditto for viewWillAppear.
In viewWillLayoutSubviews, the view controller's top-level view has its correct frame, but its descendants do not.
In viewDidLayoutSubviews, the view controller's immediate subviews have their correct frames, but more distant descendants (“grandchildren” and so forth) don't.
If the image view is a direct subview of the view controller's view, then its frame is up to date in viewDidLayoutSubviews.
If the image view is a more distant descendant, then there is no method you can override in the view controller that will be called after the image view's frame has been updated but before the image view is visible on screen. Here are two options in this case:
Create a custom subclass of UIView to be the superview of the image view. When the superview's layoutSubviews runs, after it calls super.layoutSubviews, the image view's frame is up to date.
Create a hidden UIView that is a direct subview of the view controller's top-level view. Use constraints to make this hidden view's frame exactly match the image view's frame. Hidden views participate in layout, so when viewDidLayoutSubviews is called, this hidden view's frame is up to date, and is the same as the image view's frame will eventually be (except that the hidden view's frame is in the top-level view's geometry, which might be different than the geometry of the image view's superview).
I have a UIStackView, in it there's a subView called A. I want to rotate the view A. But nothing happened. I can rotate the A's layer, it's OK. So how to make animation of subviews of UIStackView?
Once you add an "arranged subview" to a stack view, its frame is automatically managed by the constraints added implicitly by the stack view. While you could hypothetically add additional constraints which could be animated, there's no set of constraints that would allow you to accomplish a rotation.
One solution would be to nest another view inside the one that's being managed by the stack. You could then override layoutSubviews in the outer view to opt out of auto layout for the inner view, so you can manage its rotation. Whatever updates that you make there to the inner view's frame could then be placed inside an animation block.
EDIT:
According to the docs for the transform property of UIView, "In iOS 8.0 and later, the transform property does not affect Auto Layout. Auto layout calculates a view’s alignment rectangle based on its untransformed frame." That seems to imply that a rotation should work even in the presence of auto layout constraints. That said, my guess is that transforming the view is really just a convenience for transforming the underlying layer.
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.
I have a number of custom UIViews.
I constantly find myself initializing properties in the init of my custom view, but I also set the frame there too.
I usually leave my layoutSubviews empty. If I don't expect my view bounds to change, is it ok to have my various subview frames set in the init itself, or should I move that to layoutSubviews?
I wanted to mention that one of the reasons, I do it this way is because often I find myself having to calculate the frame (size) of my custom view based on how my subviews get laid out.
I usually resize my custom view's frame after all of my subview frame sizes have been set.
You should avoid allocating/creating your views in layoutSubviews method, because it will be called a lot of times. You can allocate your views in your initializer methods and layout them in layoutSubviews method. But if your views' frames are non-relative to your view's bounds, then there is nothing to worry about setting their frames in your initializer methods.
I have a containing view called containerView which is a UIView. When the app starts, it's just that view. During the course of the app's execution, I want to swap two "full-size" sub views in and out of the main containerView.
My question is, how do I make sure that the sub views fill up the entire containerView regardless of the orientation of the iPad. The containerView is 300 wide, but the height varies based on orientation.
I've tried:
setting the frame of the subview's from viewDidLoad, viewWillAppear, viewDidAppear directly equal to the side of the containerView frame,
creating LayoutConstraints that force the subview to conform to the containerView's proportions from viewDidLoad, viewWillAppear, and viewDidAppear. The question here is when to apply these constraints, and since the view is removed periodically, when to reapply the constraints.
I don't know what the idiomatic approach is and I want my code to be maintainable and reusable. Am i approaching this the wrong way? Is there some other way to make sure a subview fills up its containing view?
Example Code:
https://gist.github.com/Sahasrara/6817105
You should set the frame size in viewDidLayoutSubviews. By then all the autolayout nonsense has occurred.