TableView and autolayout, wrong frame for headers - ios

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

Related

iOS incorrect frame size at runtime

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

When is the frame size of a UIButton finally set

I made a custom subclass of UIButton so I can add an extra label. When this button is in an UIStackView, the extra label is not being sized properly. I tried programmatically adding constraints to parent button. This worked but I had pages of broken constraint warnings (slight exaggeration). If I knew when the button has been assigned a final size, I can set the added labels frame.
When is the size of a UIButton known after all the auto layout stuff is done?
As per my comments above, the frame of a button should be known and can be used when layoutSubviews() is called, this can be overridden in your custom class
viewDidLayoutSubViews is the method which finish last. While assigning auto layout constraints it gives you proper Rect information. You can use this to set constraints.
Try this: put "print("frame is: ", button.frame," in method name")" in the following methods.
viewDidLoad
viewDidLayoutSubviews
viewWillLayoutSubviews
viewWillAppear
viewDidAppear
You'll notice that the actual final frame of the button doesn't happen until the final call of viewDidLayoutSubviews and then viewDidAppear, and that viewDidLayoutSubviews is called a number of times before that, which is especially annoying when you're trying to resize the font on this label according to the size of the button's title label.
If the stack view is messing with your sizing, drag a UIView into the stack view and put the button in it, then you can constrain the button to that view.

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.

How to get actual frame of subView of root view in ViewControtroller

I have a view and a subView which is of type UIView with constraints so that it's width is equal to its superview.(I use auto Layout)
However,when i try to get frame property of this subview in viewController in
- (void)viewDidLoad {
NSLog(#"%#",NSStringFromCGRect(self.subview.frame));//get subview's frame
}
method,I always get {{0, 20}, {600, 320}} which means its width is 600.
But the actual width is 320,did this mean that this subview hasn't been layouted when this method is called,and how could I get this actual width in this method?
Any help is appreciated,Thanks.
subview's frame isn't valid in viewDidLoad -- it hasn't been laid out yet. Move your code to viewDidLayoutSubviews.
If you find you have a dependency which requires you to look at the frame in viewDidLoad, you probably don't have your constraints designed correctly. Look for areas where you can add more relationships between views. If that doesn't help, try modifying constraints programmatically -- when using autolayout, you almost never want to look at or explicitly change a view's frame.

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