iOS incorrect frame size at runtime - ios

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).

Related

Need assistance regarding viewDidLayoutSubviews

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.

TableView and autolayout, wrong frame for headers

I'm adding a view controller with a table view as a subview of a main view controller. I'm setting the constraints for position and size, i'm not givin a specific frame for the view, but i noticed that the header's labels are in a wrong frame, they have a x position of -30, instead of zero.
This happens only if i use constraints, if i use normal setframe method this problem doesn't happen.
I've tried with layoutIfneeded, or layoutSubviews but nothing seems to work

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.

how to make a programmatically generated subview conform to the bounds of the superview (iOS)?

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.

Is it possible to get the calculated frame given an array of constraints?

I'm trying to animate a view from a thumbnail to a fullscreen view when touched. When the view is in thumbnail mode it is a subview of the UITableView.tableFooterView. When I animate to fullscreen I move the view to the controller's root view before updating the constraints. I do this because the tableview clips the subviews.
This is working perfectly, but when I try to do the reverse animation it's not so easy. I have to first move the thumbnail view back into a subview of the UITableView.tableFooterView before I can update the constraints. I then undo the constraints (basically set them to what there where originally). This works but the animation does not look right because as soon as the view is moved back into UITableView.tableFooterView it's clipped again by the UITableView and the animation is partially hidden behind all the tableview adornments!
My idea is to get the calculated frame for the constraints and perform old fashioned frame animation and then install the constraints after.
Is there a known way to ask the layout system given an array of constraints what will the frame be without actually installing those constraints?
Thanks.
I don't think there is such a way.
Note that the constraints you define (NSLayoutConstraint) are not the only constraints. Other constraints are defined by the properties of your views (e.g. intrinsic content size, hugging priorities etc.).
I guess this is something that would work better with autoresizing instead of autolayout.
Another solution would be to use a hidden placeholder view in the table footer. When you are returning the view back into the footer, you can just ask for the frame of the placeholder.

Resources