I have an app using scroll view on a page with numerous icons within that scroll view. I don't understand auto layout very well, but I have constrained every single icon to its current canvas value to the view (after measuring and placing each icon in the correct spot). Why are there still conflicts (188 to be precise) and unambiguous positions?
As an example of what I have done in case the above is not clear, I have placed an icon in the top left of the scroll view. I have then constrained that icon on top, bottom, left and right of its current canvas value from the view.
This extremely hard to explain without showing (as in "live-demonstrating"), but I'll give it a try:
Autolayout for Scrollviews works differently than you probably think. You can't simply specify where to place its contents in relation to its bounds like that. After all, the "canvas" of the scroll view determines its size from the content you scroll over, whereas the size of the actual scroll view (lets call it its viewport, or "window onto the canvas") is determined in a different way (for example by constraining it to the elements around it).
This the opposite of how autolayout works for other views, since they are usually constrained by the size of their parent (up to the top view of a view controller and/or the screen bounds).
What you want to do is add all your items in a single view. From the smallest known size, constrain them to one another. Finally their container has a size resulting from its contents, i.e. your items. This is then the size of the "canvas" of the scroll view, so add it and constrain its position somehow (for example centering it horizontally and vertically). Now, the actual size of the scroll view and its position should be constrained by elements surrounding it.
It might take some thinking and training, so maybe you will want to test around in a small training project that only has a limited number of views to figure it out.
Add scroll view on the screen with constraints to parent view
Add content inside of scroll view
Put constraints between added view and parent view (NOT SCROLL VIEW)
Related
I have a ViewController which contains a ScrollView and I'm trying to make a form menu so I need multiple labels and text fields.
I've been using the designer to do that.
The ScrollView will allow the user to scroll vertically, I already have many elements in the View but I need to add more.
The problem is that the size of the parent ViewController have a fixed size in the designer and because of that I reached the situation where I need to add elements under a label but there is just no space in the designer where I can put it.
hmmm.. not sure how this will carry over to tamarin/visual-studio, but in Xcode Interface Builder you can set the View Controller Simulated Size to "Freeform" --- and then set it as tall as you wish. Well, it may have a limit, but I just tested it and made it as tall as 5,000 pts. After laying everything out, you can set it back to "normal" size. At runtime, it will size itself however you've set the constraints.
Other alternatives...
Design your elements in "containing" views, and then add them in either dynamically via code or by manually setting the positions in the designer.
Use a single, tall "containing" view in your Scroll View. Make that view, say, 1500 its tall. In the designer, add the first few elements, then set the Y position of that view to a negative number. If your Scroll View is 500 its tall, set the Y position of the containing view to -400. The view will "slide up" in the Scroll View, and you can continue adding elements.
I am a pretty new iOS developer and am coming across my first need for a scroll view. The page I need to design is a little complex. Since I was struggling to get that to layout correctly, I decided to create a super simple scene just so I could make sure I understood how to get UIScrollViews to work. Apparently it didn't help as things aren't working and I am stuck after following several tutorials.
I'm working in Xcode 8.1 and Swift 3.
Screenshots at the end of this post.
I have a scene that consists of a scrollview and a child view with two labels in it. I’ve set the labels to be ~700pt apart to try to make scrolling happen. Nothing scrolls and you can only see the first label. Additionally, the child view does not expand to be full height.
You can see in the screen shots that my scroll view has constraints to pin it to the sides of the superview.
The child view has the same.
The label constraints position them within the child view and 700pt from each other. I thought that this would give the views the height they need to make scrolling happen. There are no constraint errors.
I am hoping for the red childview to fill the vertical space and then scroll. At this point I’d take any layout as long as something was scrolling. Nothing is though, what do I not get?
Screenshots:
(removed due to link limit because I'm still a new SO user)
EDIT (6/12/16):
I've made some changes and gotten a little closer. Primarily, it was suggested to me elsewhere to set one of the labels to be equal height with the scroll view. This now gives me the "bounce" effect which means stuff is sort of scrolling; however, we're still only dealing with one screen of content as the second label which is hidden below is clipped off.
Here's where things stand:
edited hierarchy
edited screenshot
When you are using a UIScrollView in a storyboard, you need to ensure that the scroll view is able to compute the size of its content. If you don't have sufficient constraints then you will get an error in Interface Builder:
Scrollable Content Size Ambiguity
Clicking the Info icon on this error will advise you that there needs to be constraints touching all sides of the scroll view and to ensure that you can trace a continual line of constraints from left-to-right and top-to-bottom.
You can achieve this with or without the content view you have added. I will show you how to do it without the content view in scroll view, simply because there are fewer constraints that way and therefore less typing.
Add the scroll view to the root view
Constrain the top/left/top/bottom of the scroll view to its superview (the root view). Remember to turn off constrain to margins if you want the full width of the screen
Add label 1 and label 2 to the scroll view
Constrain top/leading/bottom of label 1 to the scroll view
Constrain top/trailing/bottom of label 2 to the scroll view
Constrain the trailing edge of label 1 to the leading edge of label 2 with 0 space
Constrain label 1 width to be equal to the width of the scroll view
Constrain label 1 height to be equal to the height of the scroll view
Constrain the width and height of label 2 to be equal to the width and height of label 1
There is no step 10 :)
ScrollViews are particular in that they like to know explicitly how much they are supposed to scroll. The best way I have found to handle this is to have the following hierarchy with some constraints:
-Scroll View
-Content View
-View (constrained to top, bottom, leading, trailing anchors)
-Your other views (e.g. Label)
By having one View living underneath the Content View and then containing all of your other Views within that View, the ScrollView then knows how much it's supposed to scroll (it just uses the size of the one child View) no matter how much stuff you have inside of the child View.
Let me see if I can snap a picture of an example from one of my projects. In the meantime, give this hierarchy a try and let me know if it works for you. You would probably constrain the Label to the top and leading anchors of the child View and then constrain the height to something taller than the screen (e.g. 1000 units).
Let me know if you have any questions.
Edit: Example hierarchy below
I have a simple screen, with a slider and a label positioned next to each other horizontally. I have embedded these inside a UIScrollView (I set this to fill the screen and used 'Add missing constraints'), because I will need vertical scrolling later down the line. I don't however, want horizontal scrolling. I have seen numerous posts on here and other sources about people wanting to disable horizontal scrolling, however I'm not sure that's what I want to do, I think I need to restrict the UISlider from causing the horizontal scrolling; I think it is trying to take up more width than the screen. I have added what I think are the necessary horizontal constraints:
Leading space to container for the UISlider
Horizontal spacing to the UILabel, and
Trailing space to container for the UILabel
But this still causes horizontal scrolling, and the UISlider's are the cause, they are taking up more room than I want, as seen below:
I have tried disabling horizontal scrolling in the code using a few techniques, one being:
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentOffset.x>0 {
scrollView.contentOffset.x = 0
}
}
but this does not seem to stop the horizontal scrolling.
Can anyone offer any suggestions?
Thanks in advance.
My suggestion is to never use Add missing constraints. It never does what you really want.
Here's the problem. You are laying out your UI on a ViewController in the Storyboard that is square. Apple did this to remind you that you need to be flexible in your design, but it doesn't match the size of any device. When you Add missing constraints, it uses the absolute dimensions of that square to create the constraints which are certainly wrong.
In your specific case, it is giving the slider a width that is too wide, which is why the slider goes off the right side of your screen.
Here's the trick about scroll views. If the contents inside of a scroll view are wider than the scroll view itself, then that content will scroll. The same applies vertically: if the contents inside of a scroll view are taller than the scroll view, then the contents will scroll.
In order to design this to work on all phones, you need to make sure that the contents of the scroll view are laid out correctly for each phone size. Which certainly means you don't want to use specific widths for both the label and the slider because you'll end up with the wrong width for some device, if not all of them.
The best way to do this is to:
Drag out the scroll view and add it to your ViewController. Add constraints to make sure it is properly sized on all phones, such as attaching it on all sides to its superview with a fixed distance.
Drag out a new UIView and drop it on the scroll view. Drag its edges until it exactly matches the size of the scroll view. This will be your content view. Pin all four edges of this content view to the scroll view with offsets of 0.
Here's a tricky bit. Even though you've pinned the content view to the scroll view, its size of free to grow because that is what allows it to be bigger than the scroll view itself and allow there to be content to scroll over. To keep your scroll view from scrolling horizontally, you need to make sure the content view has the same width as the scroll view on all devices. To do that, find the scroll view and the content view in the Document Outline to the left of the Storyboard. Control-drag from the content view to the scroll view and select Equal Widths from the pop-up.
You still haven't told your content view how tall it should be. For now, give it an explicit height constraint of 1000. That will be enough to scroll.
Now, add your label and slider to the content view. In addition to constraining them to each other and to the edges of the content view, you will need to give your label a width constraint. Then Auto Layout will have all of the information it needs to compute the width of your slider. Auto Layout knows how wide the content view is (which will be different on different devices), it knows how wide your label is, and how far everything is from everything else, so it will just stretch the slider to fill in the rest.
If you do all of this, you will have a UI that is properly sized for all devices in all orientations that scrolls vertically.
Just embed all view in your UIScrollView in a UIView, give it the required constraints then the slider and label will stay.
That worked for me just now.
UIScrollView is special when you want use AutoLayout with it, subviews can not be added directly, it needs a container view to constraint the contentSize of UIScrollView, Auto Layout Guide:Working with Scroll Views explains the detail reason, and you can find many solutions to solve UIScrollView's auto layout on Google, Such as this answer.
To be honest, it's confused and complicated to understand UIScrollView's auto layout, but if you overcome this, others auto layout question is easy to resolve.
I implemented a viewcontroller similar to the one in this question:
iOS two views cover exactly half of parent view
I get the desired result. The problem is that when I add a UIImageView into those two containers, the result get resized and ruins the symmetry. How do I prevent Imageviews from changing the size of their containers? I need to use AspectFill for these images.
I don't think the containers' sizes are changed. You just need to set their clipsToBounds property to true to avoid overflow.
If you use Reveal you should see the containers' sizes are not changed even if pictures inside them are bigger than themselves.
I'm unsure I understand what you mean about adding UIImageView into those controllers causing the views to resize, so forgive me if I'm getting this all wrong...
It sounds as though you've created two views of equal height that, together, consume the entire vertical space of the screen. After having done that, you want to add one or more UIImageView's to each of the original two views. Unfortunately, when you add the UIImageView, the enclosing view is resized.
Assuming I have that correct...
Are you doing this with Interface Builder either in an XIB or Storyboard file? If so, you ought to be able to achieve this with the proper set of constraints.
In the following image, I've laid out what I describe above.
As you can see, I have a red view on the top half of the window and a green view on the bottom half. The red view contains a UIImageView that is 75% of the width and height of red view, with its origin at (20, 20) within the red view.
The scene is configured as shown below:
The constraints on "Upper View" are:
You can see from this that Upper View is flush with the left, right, and top of its superview, and that its space to Bottom View is 0. You'll have to trust me that Bottom View is set up the same way.
The height of the Upper and Lower views is "Proportional" as shown in this constraint:
To achieve this "Proportional" setting, you first make the height of Upper View equal to the height of the superview, and then edit the constraint, changing "Multiplier" from "1" to "0.5."
The height (and width) of the Image View is proportional to that of the Upper view, as shown here:
If you set it up this way, you ought to be able to accomplish what (I think) you are looking to accomplish.
If my original assumption of what you are trying to achieve is incorrect, please post images of what you've got and how it's not working.
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