I am loading an HTML document into a UIWebView with loadRequest:requestURL. The HTML content does not fit entirely into the view's frame. What surprises me that at the time the page has been loaded (when webViewDidFinishLoad:is called) the view's scrollView.contentOffset is different from (0,0). It looks as if the content may have been scrolled down approximately so that its vertical center is aligned with the view's vertical center (see screenshot below).
How can this happen and how can I achieve the desired behavior of having the content's top left corner aligned with the view's top left corner initially? Setting scrollView.contentOffset to (0,0) in webViewDidFinishLoad:` is an option but apparently leads to some flickering.
This is occurring in the context of a UIViewController that contains two sibling UIWebViews below its root view. The two UIWebViews are vertically stacked with a UILabel (appears in screenshot with gray background) in between. (The lower UIWebView is not visible in the screenshot, because it's currently all white.) The view uses constraint-based layout and the current problem concerns the upper UIWebView.
UPDATE Further study has revealed that the visual defect is not due to scrolling in the upper UIWebView but due to its frame.origin.y having been set from 0 to 64 (which is also the combined height of the status bar and the navigation bar) by the time webViewDidFinishLoad is called. I am calling loadRequest:requestWithURL: from the view controller's viewWillAppear: and there is a layout constraint on the upper UIWebView that imposes a vertical space of 0 between its top and the bottom of the Top Layout Guide.
I think what happens is the following: iOS 7 wants UIScrollViews to extend underneath the status and navigation bars when scrolled downwards. Apparently it performs the initial vertical scrolling as a means for preventing the top part of web content from being hidden behind these bars before any scrolling occurs.
I had a layout constraint for a vertical distance of 0 to the top layout guide placed on the UIWebView which led to the wrong vertical distance of twice the height of the status bar plus the height navigation bar from the top of the screen. Replacing this layout constraint by one for a vertical distance of 0 from the superview corrected the wrong.
Related
I have to lay out a few views in a View Controller which should look like this when run.
Initially the white view should be partially covering the blue view at the bottom like shown in the image. The blue view should stay put while the white view at the bottom can be scrolled over the blue view.
I added the blue view to the main UIView of the view controller. Then added a UIScrollView on top of blue view and added the white view on to the scroll view.
Now I'm getting the dreaded ambigeous content size error. I have pinned the scrollview on all four sides. Then added leading, bottom and trailing constraints to the white view. Then I tried adding a top constraint to the white view but the error still persisted. I also adding a height constraint to the white view to no avail.
It might be dofficult to imagine my setup so I added a demo project here as well.
I set the constraints for you here.
Explanation:
You are getting ambigous content size because your scrollView does not know its content (white view's) width and height.
I added top,width and height constraint to white view. This way your scrollView knows its content width and will scroll only vertically. As for content height - u can change heightContstrait's constant in code, or completely remove height constraint if you use autolayout properly for white view's subviews.
Put the blue view under scrollview. And set scrollviews content inset, to make your white view appear not on top, but with some space. Now on start you get white view a bit over blue, and on scroll it covers blue view. About size errors, scrollview needs to know it’s content size initially. I assume your white view is table view, so set it’s bottom constraint to zero. If you want blue size to change it’s size on scrolling, like it’s avatar or something, it’s a bit different. You should take scrolling events on uiscrolldelegate, and change blue views height accordingly
Make sure you've read (and understand) Apple TN2154: "UIScrollView And Autolayout", which explains the steps required.
Looks like you're not specifying the content size properly.
I have this following screen:
All of the views are inside an UIStackView. The yellow view would be circular on runtime. My issue when I run an a smaller iPhone (5S), the light blue view which contains the yellow view, will not get smaller, so the red and blue view from the bottom will shrink. I want these two and the green view to have fixed width and the light blue view to adapt height.
But for some reason it doesn't. I am sure is because of the constraints I set to the yellow view. But I can't figure out which one is the problem.
Here are the yellow view constraints:
The constraints for the other views are:
Green view: height = 64
Red and blue view: height = 50
Much appreciate if someone could take some time to look over this constraints and help me understand how to make the light blue (and yellow) to change height when screen changes.
Did you try lowering the Content Compression Resistance Priority (vertical) for the light blue and yellow views to be less than 750, and the Content Compression Resistance Priority (vertical) for the green, red and blue views to be higher than 750 (maybe even 1000 / required if that's the case?)
UPDATE
I took a look at your project. Actually, the issue doesn't seem to be with your stack views, constraints, or the DailyStatusViewController at all. Instead, the issue looks more related to the MainViewController which is embedding the Daily Status view inside a scroll view. The scroll view is both clipping to bounds, and is placed behind another container view. If you turn off clip to bounds on the scroll view, and move the container view below it to be behind it, you will see your stack views laid out correctly, but were just being clipped.
As to why they were clipped, keep in mind that your out stack view has a required height constraint of 300, while the scroll view it's being embedded in has a height constraint to 45% of the screen height. So on smaller screens, there is less than 300 points available to display the Daily Status view, and so it is being clipped. You probably want to remove the fixed height constraint from your outer stack view and instead constrain it to the edges of the root view.
In an iOS 8 app, I'm trying to create a screen that has a "parallax header", i.e., an image header that grows as you pull down. I would like to do this using only constraints in Interface Builder, if possible.
Here is a nice guide by Pete Hare on how to do such a thing, and I've also had good help at looking at this example project by Bill Carson. However, contrary to these projects, this is not the header to a scrolling area that's taller than the screen, like a Table View; it's just one page. And for some reason, I can't get things to work in my app. I find Scroll Views in Interface Builder rather confusing to begin with. Could anyone walk me through the steps?
Why, certainly! First we'll set up a view controller with a scroll view.
Create an empty View Controller. Give its initial View the Xcode Specific Label Root View so we can tell things apart.
Add a Scroll View. Resize it to fill the view controller, and add constraints for Leading, Trailing, Top and Bottom to equal the superview's corresponding edges. (I do this by control-dragging from Scroll View to View in the Document Outline, hold down the Option and Shift key and then select all four edges, and then Add Constraints.)
Enable Bounce Vertically in the Scroll View's Attribute Inspector.
Add a simple View to the Scroll View and pin its edges to its superview -- the Scroll View -- in the exact same way as we did with the Scroll View to Root View (although this time we don't need to hold Option key when adding constraints). Give the new view the Xcode Specific Label Scrolling Content.
Xcode is not happy, it says Scroll View is missing constraints: "needs constraints for X position or width" and "needs constraints for Y position or height". Let it automatically add missing constraints, it will add constraints for the center of Scrolling Content to the center of Scroll View, in X and Y directions.
By setting a background color to the Scrolling Content, we can now run and confirm that the scroll view with vertical bounce is working as intended. Nice. Now, let's add the header.
Add a nice header image to the project assets, and drag an Image View to the Scrolling Content. Label it Header Image. Select your image asset as the Header Image's image. Drag the corners of the Image View so that it is aligned to the top, left and right edges of your view. Now let's go through the constraints to set on the Image View.
We want the top edge to be fixed to the top of the screen, regardless of how the user is scrolling. So we need it to be pinned to something outside of the Scroll View. You may try fixing it to the Root View's top edge, but unfortunately, that does not work for some reason. What does work is to pin it to the Top Layout Guide. You'll do this easiest by control-dragging in the Document Outline, between the Header Image and the Top Layout Guide and accpet the suggested constant.
The bottom edge of the Header Image needs to be pinned to something inside the Scroll View. Ultimately, we'd like it to be set to a fixed distance from the top of the Scrolling Content -- but Interface Builder won't let us do that. You can only set it to some distance from the bottom of the Scrolling Content, which is not very practical since you will then have to take the height of the device into account. What we instead do is to add another view directly beneath the Header Image, pin that view's top edge to some distance from the Scrolling Content's top edge, and then pin the Header Image's bottom edge to our new view's top edge. This could be a regular View that holds the rest of your user interface below the header. But for this example (and to demonstrate a later point in this guide), we'll use a label. Add a label directly beneath Header Image and give it three constraints: pin it's top edge to the Header Image's bottom edge, it's top edge also to the Scrolling Content's top edge, and it's center X to the Scrolling Content's center X. The distance between the label's top edge and Scrolling Content's top edge will be the height of the image in the non-dragged state. This unfortunately needs to be set to a constant in the storyboard file -- we'll have to update it programmatically. More on that later.
The last part is easy: pin the leading and trailing edges of the Header Image to the leading and trailing edges of the Scrolling Content.
Now, all our constraints are in place! If you run the app, you can see how it's working correctly constraintwise, but the image isn't scaling the way we expect it to. A couple of last tweaks before we're done.
In the Attribute Inspector, under View, set the Mode of the Image View to "Aspect Fill". That gives it the correct parallaxy behavior.
To set the height of the Header Image correctly, we need a little bit of code. First, make an outlet from the constraint between the label's top edge and the Scrolling Content's top edge -- the one we set to a fixed value -- to your View Controller's source file. Call it imageHeightConstraint. Also add an outlet from the Image View called headerImageView. A good place to update the constraint programmatically is in the viewWillLayoutSubviews View Controller delegate method. Here is some code in Objective C:
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
CGSize imageSize = self.headerImageView.image.size;
CGFloat heightForWidth = imageSize.height / imageSize.width;
CGRect screenBounds = [UIScreen mainScreen].bounds;
CGFloat screenWidth = CGRectGetWidth(screenBounds);
self.imageHeightConstraint.constant = screenWidth * heightForWidth;
}
Finally, you may notice that when scrolling up, the label -- or whatever content is underneath the header view -- is getting covered by it. This is solved by checking Clip Supbviews on the Image View's Attribute Inspector.
Whew!
(Note: I began writing this as a question and then kept writing while I solved it for myself. I guess it would do well with a little text pruning and some images, but maybe it will help someone...)
I have a UIScrollView that fills the width of the device and it contains several UIViews laid out horizontally. The views all have the same width, so on the iPhone one has to scroll to see all the views, but on the iPad all of the views are visible.
My question is how can I perfectly horizontally center the views on screen? When the available space is large enough to display all views, they need to be centered but when it's not large enough to display all views they can be laid out as they are now, simply left to right.
The interface has been set up entirely in Interface Builder. The scroll view is set to fill the entire device width - leading and trailing to superview. The first view in the scroll view has leading set to its superview so it's stuck to the far left. The last button has its trailing set to the superview - to the far right of the scroll view, to define the scrollable content area. Each view in the middle is laid out relative to the view to the left of it - leading to the previous button.
Here's a graphical representation of the current layout:
Here's a graphical representation of what I'd like to obtain:
Additional info:
The scroll view doesn't have to always fill the device width, as it is actually completely transparent. When all views are visible I want to disable scrolling. This will need to be adaptive, such that upon rotating the device it can change the layout if needed, because on iPhone in landscape all the views should be visible but in portrait they won't be.
You can try setting the contentInset property of the scroll view, which has a type of UIEdgeInsets. This property simply provides the scroll view with a certain amount of "padding", adding to the scrollable area.
You can determine how much extra space you have at the end (right-hand side) of your view, divide it by 2, and use that value to provide equal-width padding on each side.
Another purely autolayout-based approach could be:
Constrain your middle view to be horizontally centered in the superview.
Constrain each view relative to the middle view in a chain
I am new to auto layout and need to resize some views when it rotates to landscape.
My view hierarchy from top to bottom is:
Toolbar (user info)
Toolbar (scroll view #1 title)
Scrollview #1
Toolbar (scroll view #2 title)
Scrollview #2
All views are adjacent to each other and when rotated stay in the same layout.
However, I need to shrink both scroll views by a small amount so everything fits onto the screen when in landscape mode.
What is the proper way to do this?
I have tried manually changing the scrollview frames in a orientation change method, but the view size doesn't change.
Give the toolbars explicit heights.
Pin the top tool bar to top and sides.
Pin bottom scrollView to sides and bottom
Make the height of the 2 scrollViews the same.
Make all of the view have vertical spacing constraints between them (you should get this automatically, if you put them right on top of each other when you drag them in).
This should be sufficient to define every views size and position. When the view gets smaller on rotation, the only thing free to change will be the scroll view heights.