ios Autolayout sizeThatFits Issue With View Load Order... or Something - ios

Intro:
The whole metrics table you see here is a view within a metrics viewcontroller.
There is a nearly identical table below in green (doesn't get displayed properly as you can see).
The red area and green area are actually just container UIViews for the viewcontroller's views.
The problem: I must use autolayout for my project. The size of these tables is dynamic (vertically).
It is my understanding that autolayout should be able to determine height automatically for UIViews (using intrinsicContentSize) as opposed to me manually constraining heights. The actual best height of the table in this example is 192. However, when calling sizeThatFits, it returns 405 on the viewcontroller's view. After hooking up the more button to a test method, I found that by calling sizeThatFits on the subviews of the viewcontroller's view, it properly returns a sum of 192. So, I can at least manually constrain the height by using a method to determine the sizeThatFits on the subviews, right..?
Unfortunately, this sizeThatFits method only seems to be able to work once the view has totally loaded on the screen (which explains why it works when using the "more" button test method).
If I attempt to manually determine the sizeThatFits automatically when the view loads, it returns 0 for all views. There seems to be an issue I'm not familiar with when it comes to the order of views loading or something. At what point does this method actually work?

Related

UIScrollView and two-way scrolling

I'm having some trouble working with UIScrollView in my current project. My intention is to have a variable-size drawable canvas which can extend beyond both sides of the screen, and whose size is determined by the user at runtime. For this purpose, I put in a UIScrollView with a Content View inside of it in my View Controller. Unfortunately, Interface Builder keeps pestering me to define the X and Y axes of my Content View as a constant sizes which isn't possible given my specifications. I've even set the ScrollView.contentSize programmatically in my viewDidLoad method and Interface builder is still giving me errors. All other tutorials I've seen have only talked about one-way scrolling with UIScrollView, so here I am. Here's some screenshots of my storyboard hierarchy as well to make things more clear:
Hierarchy
Errors
Does anyone have any experience/workarounds for this kind of problem?
Interface builder is TRYING to be helpful and make sure that you don't have arbitrary constraints on your content view.
Remember that Interface Builder just cares about the initial state of the views it loads. It does not care what you do with them after they have been loaded.
Make IB happy by giving it some rational initial size for your content view. Then programatically after your scroll view loads you can resize the content view and set the scroll view's content size programmatically.

Xcode embed all direct in scrollview without content view

I'm using Xcode 6.4 and I'm struggling with a large page with a bunch of various labels and text fields etc., that has to be scrollable. I looked at various tutorials some of which are obviously outdated and I had all my elements at first in a view that is inside of a scroll view. But that was a big pain with getting all the elements aligned properly using Auto Layout, in addition to not getting it to scroll either.
Now I see one person recommend using the following directly on the elements, without having a content view at all just a scroll view:
Editor -> Embed In -> Scroll View
I like it for the fact that nothing seems out of proportion, all the elements are shown on the iPhone exactly as I have it on the Storyboard without having to set any constraints, however I'm not able to get it to scroll.
This is my class
class ResNotFoundViewController: UIViewController, UIScrollViewDelegate
and I also try to set the height of the scrollview in the code as so:
#IBOutlet weak var scrollviewoutlet: UIScrollView!
and then this in the viewDidLoad as so:
scrollviewoutlet.contentSize = CGSizeMake(375, 840)
.
But I'm not able to get it to scroll at all, it will stay exactly like it opens up with From: Label being the last visible elements.
What am I doing wrong?
You've located the problem, thanks to your logging. (Don't worry about the double call - how many times and when viewDidLayoutSubviews gets called is uninteresting.)
The problem is that you've set the scroll view itself to be taller than the window. In particular, it is 375x809. But so is its contentSize. Thus there is nothing to scroll. A scroll view can only scroll if its contentSize is bigger (in one or two dimensions) than the scroll view. (Moreover, a scroll view bigger than the screen is usually somewhat pointless, since there will be parts of its content that the user will never see - even scrolled all the way, that content will be offscreen.)
So, fix the size of the scroll view itself and you'll be fine. The best way is to assign it constraints that pin its boundaries to its superview, since the superview will have different sizes on different devices.

Why would all views in a view have userInteractionEnabled set to NO

I have a view and for some unknown reason, it's not receiving any touches. When I debugged the view, I've found out that its views' userInteractionEnabled is set to NO. The problem is that, I haven't set it anywhere; neither in Interface Builder (triple checked) and code. The problem started when I first created the regular UIViewController in Interface Builder, without a subclass or any custom code. I know it's near impossible to tell something without code samples, but my project is heavily complicated, and as I've said, the problem is appearing in a regular UIViewController (no subclass) that I've set in interface builder, so there is no relevant code that would mean anything. The rest of the app just works fine, though.
What can possibly cause all views in a regular, default view controller to become userInteractionEnabled = NO?
Found the answer myself after traversing window's recursive description more carefully. I had a scroll view, and inside that, a content view, and other views inside. I'm on pure auto layout, so my scroll view's content view needs to calculate it's own intrinsic size. I was using a placeholder height for the content view in Interface Builder, to make editing interface visually easier. Apparently, I forgot to connect the last view inside my content view to bottom of my content view with a constraint, resulting in my content view having a height of 0 (though still displaying perfectly as it doesn't have clipping enabled). When it's size was 0, it was calculating userInteractionEnabled as NO automatically without being explicitly set to NO. I've added the required bottom constraint and the problem went away.

iOS Auto Layout UIView Drawing cycle

In one of the WWDC videos, Apple said that the layout is done from top down, ie from superview to subview (after constraints are calculated from bottom up). Display is also done from top down.
My questions are:
1. At what point in the viewcontroller is a view's frame (origin and size) determined? I tried to log the size of a view (defined using auto layout), but it was always 0 0 0 0, which is odd, because the view is already generated in the simulator;
For an autoresized view, when is view.frame available?
Same question, except this time it is UIImageView.frame. I tried to log to console, even though the size is fit into the constraints, the logged UIImageView frame is 0 0 width_of_original_image height_of_original_image. But for other views like labels, the frame is printed correctly on the console.
It seems like that there is a mysterious auto layout engine that performs transform, and nobody knows what is going on inside the engine, but to check what is thrown onto the simulator display to figure out how the view was rendered by this engine???
It's not mysterious. It's quite simple! Think of constraints as instructions written down on pieces of paper - the views. Every once in a while, it's layout time! The runtime collects the pieces of paper from the views in order and obeys them - and so you end up with laid out frames.
So if you check sizes of things before layout time, you get the wrong answer because it hasn't happened yet.
And when is layout time? It's whenever the runtime sends views layoutSubviews - in fact, the runtime obeys constraints and performs layout during layoutSubviews. And your view controller can hear about this before or after, with viewWillLayoutSubviews and viewDidLayoutSubviews.
I think the part that confuses beginners the most is what happens when a view controller comes into existence. viewDidLoad means it has a view, but that is all; neither the view nor its subviews are in the interface yet, so obviously there can be no layout. Considerably later, we get viewWillAppear:, the view goes into the interface, and now we get layout. So if you check sizes in, say, viewDidAppear:, they will be right.

iOS - viewDidLayoutSubviews called before auto-layout completed on iOS7

We're currently having a problem that only seems to affect iOS7 devices.
Within our .xib file we have two views within a container view (i.e.: not at the top level of the view hierarchy) that need to be circular on display. The views have constraints applied to their position and horizontal spacing within the container, and an aspect ratio condition requiring they are square. The views should expand in width/height on larger screen sizes, respecting the constraints described.
In our VC, we have the following in viewDidLayoutSubviews to force these views to appear circular:
- (void)viewDidLayoutSubviews {
self.progressContentContainerView.layer.cornerRadius = self.progressContentContainerView.frame.size.width/2;
}
This seems to work fine on iOS8, however on iOS7 there is a period after the view has been displayed where the constraints have not yet been applied and the size of the view/views is incorrect (see attached screenshots). This resolves itself and correctly renders a circle after half a second. This only appears to happen when the views that we intend to be circular are NOT at the top level of the VC's view hierarchy which seems to imply that viewDidLayoutSubviews is called before the subviews of subviews have also been laid out.
My guess is that we could potentially fix this issue by subclassing UIView for the nested container, adding references to the circular view within this subclass and overriding viewDidLayoutSubviews here to make the cornerRadius adjustment. This seems like a bit of a workaround though and I'm interested to see if there are other options.
Is there a cleaner/more idiomatic solution to this problem?
I know this is an old question but have you tried calling either:
[self.progressContentContainerView setNeedsUpdateConstraints];
or:
[self.progressContentContainerView layoutIfNeeded];

Resources