UIScrollView Paging Autolayout & Storyboard - ios

There are plenty of answers regarding scroll views with autolayout and plenty about scrollview paging, but I can't find a single thing that addresses both.
I'm not trying to do anything fancy...just 7 full-screen image views that I would like to scroll horizontally with paging, but for the sake of simplicity (ha!), I decided to attempt it all right in the storyboard.
The controller is set to freeform size with a width of 2240 (320*7). I then set it up the way Apple suggests for autolayout...
UIScrollview
/-----UIView
/----------Content (7 image views)
The scrollview has 0/0/0/0 constraints to all edges, as does the UIView inside.
When Paging Enabled is off, it behaves beautifully - exactly as expected. But once I turn Paging on, a swipe makes the view go crazy, scrolling the entire 2240 width, and then bouncing back and eventually landing on the proper page.
I know I have the tried-and-true option of just scrapping it all and doing it programmatically, but my stubbornness wants to figure this out. It must be possible!

I have a UIScrollView with paging and AutoLayout working perfectly fine. Here is my set up:
UIView // Main view
|---> Dummy UIView // See below
|---> UIScrollView
|---> Content UIView
|---> Page 1 Container
|---> Page 2 Container
The constraints I used are
Dummy UIView -> Parent UIView is whatever I want the size of the paging scrollview to be, and UIScrollView -> Dummy UIView is (0,0,0,0) on all sides. This is just regular auto layout stuff which creates a dummy UIView where I want to put the scrollview and a UIScrollView which completely fills the dummy UIView.
Refer to the Technote from Apple for AutoLayout and UIScrollViews: https://developer.apple.com/library/content/technotes/tn2154/_index.html
The content inside the scrollview has to have an intrinsic size. It cannot rely on the scrollview to get its size.
As indicated in the TechNote, set the constraints from all four sides of the Content View to the UIScrollView to (0,0,0,0). The exact values don't really matter since all you are telling the UIScrollView is that this is the view to get the contentSize from.
At this point Xcode will complain that Content View has no intrinsic size. And here is the trick: This is where we use the Dummy UIView that we created above. The size of the Dummy UIView is precisely the size of each page in the UIScrollView.
So we set the height of Content UIView equal to height of Dummy UIView and the width of the Content UIView equal to the number of pages times the width of the Dummy UIView using AutoLayout. (For the later change the multiplier in the constraint to be the number of pages).
Now create pages inside the Content UIView as you normally would and set Paging Enabled to yes on your UIScrollView and voila you have paging in a UIScrollView using AutoLayout.
I've tested this in IOS 6, 7 & 8.
Update:
Here is a sample project with this setup as requested:
https://github.com/kostub/PagingScrollView

Follow Working with Scroll Views to build paging UIScrollView with content in Interface Builder.
I'd also recommend using Stack View as a content view for your UIScrollView since it allows to essentially reduce layout complexity.
When you use traditional approach each entry view inside content view has 5 constraints at least:
leading to previous entry
top to parent
trailing to next entry
bottom to parent
equal width to scroll view
Stack View arrange its content automatically thus the only constraint each entry should have is "equal width to scroll view"
Check this project https://github.com/eugenebrusov/ios-stack-paging-scroll to see Stack View in action.

It is possible to use the scrollView's size to set the size of the contentView, contrary to https://developer.apple.com/library/ios/technotes/tn2154/_index.html; this was tested in iOS 8.2 beta 3.
Note that I did this programmatically, but hopefully this is useful to someone. The hierarchy is:
root: UIView
scrollView: UIScrollView
contentView: UIView
page0
page1
...
Add constraints to position scrollView relative to root and any siblings of scrollView.
Attach contentView sides to its superview (scrollView):
"H:|[contentView]|"
"V:|[contentView]|"
Add size equality constraints to contentView and scrollView; this is the part that contradicts TN2154 (which says "do not rely on the scroll view to get their size"):
contentView.height == scrollView.height
contentView.width == scrollView.width
Note: the above is made-up notation for a programmatically instantiated height constraint.
Lay out the pages relative to their superview(contentView); I did this by tacking the first page left/top to contentView left/top, and subsequent pages left/top to previous page right/top.
Credit to Koustub for getting me on the right track - his solution works, but with some fiddling I was able to eliminate the dummy view.

Related

Next Prev buttons over UIScrollView

I am having trouble with layout in Interface Builder (Xcode 9 / iOS 11).
I have a UIScrollview and over it I have 2 buttons (next and prev view).
If I put the buttons under the scroll view, they appear and work. But if I put them over the scroll view, they don't appear anymore.
What can be the problem?
Layout buttons and scrollview:
Warning message:
Drag scrollview
Constraints
You need to understand first how UIScrollView works. Check again the Warning Message. It says:
ScrollView has ambiguous scrollable content
It means that the subviews of your scrollView does not have enough constraints attached to them and to the scrollView to make the scrollView scroll. Well how does the scrollView scroll? If the content of the scrollView exceeds the frame (either width or height) of the scrollView, then the scrollView will be scrollable.
In your problem, the two buttons do not push each other to the scrollView's frame, is this clear? It means your buttons do not have enough constraints.
I'll try to provide an example in a bit.
When working in Interface Builder, if you drag and drop one object onto a container object - such as a UIView or UIScrollView (which is also a UIView), the object will become a subview of the container.
That makes it a little difficult to do what you want, which is to have the buttons on top of the scroll view, NOT as subviews of the scroll view.
As you see in this image, the buttons are clearly NOT subviews of the scroll view:
In this image, I drag/drop the buttons onto the scroll view, and they become subviews:
So, to get what you want, add the buttons outside of the scroll view and set their constraints. I gave Prev a leading constraint of 16 and Next a trailing constraint of 16... both constrained to the view, not the scroll view. Then I constrained Next to be centered-vertically to Prev so they will stay vertically aligned to each other.
Then give Prev a centered-vertically constraint to the scroll view. Select Editor -> Update Frames and the buttons will move into position, but - as you can see from the Document Outline - they are not subviews of the scroll view:
At this point, you can drag-scroll any content you add as subviews of the scroll view (such as the image view here) without affecting the buttons:
I'm assuming you want your buttons over (and not under) the scroll view. That's easy enough in Xcode 11 Interface Builder, which lets you pin subviews of scroll view (i.e., its content view(s)) to either the Frame Layout Guide or the Content Layout Guide or a combination of either.
The frame layout guide defines the size of the visible area of the content view by applying width and height constraints based on any superview other than the scroll view or its container (in other words, another sibling subview to the parent view containing the scrollview).
The content layout guide defines the size of the overall area of the content view any applying width and height constraints of based on same, which is, or course, larger than the frame of the scroll view:
Using these layout guides obviate the need to toggle scrolling in either direction; constraining the Content Layout Guide to a height or width of the visible frame of the scrollview determines whether it can be scrolled vertically or horizontally.
That means that, if you constrain a subview of the scrollview to the Frame Layout Guide, it will not scroll (and, yet, will be a true subview of the scrollview).
The
Frame and Content Layout Guides added to the UIScrollView Interface
Builder component in Xcode 11 is not yet documented; however, Apple
still distributes an out-dated document that describes how to create > a "floating view" in a scroll view with Xcode 10 or earlier.

What are the correct constraints on UIScrollView from Interface Builder? Pure Autolayout from IB

I have been trying to get ScrollView to work for 2 days now, and it doesn't work at all. Most of the suggestions here on SO and other websites say that you need to pin the ScrollView to the root view and then place a ContentView (UIView) inside ScrollView and then pin it to all sides of the ScrollView (so that the scroll size can determine the contentSize... However this does nothing). There's also conflicting information out there, one video says that there needs to be a constraint from the bottom of the ScrollView to the ContentView. Neither solution has worked for me. Here is what I've been doing in most of the combinations I've tried:
UIView -> UIScrollView
Pin all sides of the UIScrollView to the UIView
Create a UIView (name it content view) and place it inside UIScrollView
Pin all sides of the UIView to the UIScrollView
Problem at this point: UIScrollView needs constraints for X or width AND Y or width. The only thing that seems to solve the complaint is setting the UIView inside the scroll view centered horizontally and vertically, but this does nothing to make scrolling work. Another option is setting the UIView equal height and width to scroll view, but again, that does nothing other than remove the complaint.
I don't understand. Isn't pinning the sides, setting the constraints? IB seems to think that this is not the case.
What are the correct constraints needed? All I need is a simple view with stacked controls (to fill out a form) and the screen needs to be able to scroll if the form is longer than the screen.
I'm using iOS for the first time, and building purely from IB for now... minimal code solution would be best.
You are half way there. First you need to decide what you are going to display in the scrollview, you have placed a content view, that needs to have an intrinsic size. You can choose to put there static or dynamic views. Static views will have their size defined at design time, and that will resolve the UIScrollView AutoLayout constraints. If instead you are doing it at runtime with dynamic views you will need to choose a default size for your content view, create an IBOutlet for the width and/or height of your views and then resize them at runtime altering the outlet in viewDidLayoutSubviews. The video you linked explains that quite clearly.

UIScrollView and auto layout not working

I'm trying to make a view scrollable in my app.
O follow exactly the steps in tutorial https://www.youtube.com/watch?v=UnQsFlMGDsI .
The main steps I did were:
Add a UIscrollView
Add a view into UIscrollView (Content View)
Create the margin constraints with value 0 (top, left, right and botton) for UIScrollView
Create the margin constraints with value 0 (top, left, right and botton) for Content view
Create 2 constraints of "equal width" and "equal height" between ContentView and main view
... (Add some text fields)
When I run the app, the scrollView doesn't work
I really can't make it work. I tried many ways to apply a UIScrollView and didn't get it.
As mentioned in the video, the main aim is to automatically adjust the UIScrollView contentSize to the size of the screen or to the size of the content in such way that on smaller devices scrolling is active and on larger devices scrolling is not active because it is not needed.
For this to achieve you've to set proper constraints to the content view so that the height is automatically calculated. From your video its visible that you're not setting the proper constraint to the last UITextFeild (this is important).
You have to give the top as well as the bottom constraint to the last UITextField so that contentView height is automatically calculated.
I have done a sample project which you can check here :
Sample Project
you can do this for working UIScrollView:
1)add a ViewController
2)put a UIScrollView in it
3)from "Simulated Metric" change size to Freeform. change the height to your desire height
4)add auto layouts to both UIScrollView and View (it's not needed)
5)put your controls in view and add auto layouts to them.
this should work :)

Pure autolayout with a UIScrollView

I'm trying to get a UIScrollView working with autolayout positioning and sizing. I'm following the guidelines for a "Pure Auto Layout Approach" in this documentation, but I'm having a hard time getting it to work.
My hierarchy looks like this:
UICollectionViewCell -> UIView -> UIScrollView -> UIView.
Everything is positioned with autolayout constraints, including the UIScrollView (which is positioned relative to it's direct parent UIView). The UICollectionViewCell does have a fixed size eventually, as it's set by collectionView:layout:sizeForItemAtIndexPath, but I don't think that's relevant this this issue.
The issue I'm having is that the content of the last UIView (the child of the UIScrollView) isn't showing up. If I manually give it a frame size, it does show, but I really want to use autolayout.
There's a line in that documentation article that I don't quite understand:
Position and size your scroll view with constraints external to the scroll view.
Does this indicate that I can use autolayout to position and size the scroll view within it's parent, or does the scrollview have to have a fixed frame?
Autolayout with UIScrollView may seem too complicated at first. Conisder following two things:
1) UIScrollView externally behaves like any another UIView. This means, you can set any position/size to it with auto layout constraints. No differences from UIView.
2) UIScrollView internally CAN (but not should) calculate it's content size basing on given auto layout constraints inside it. For this behavior you should give one odd explicit horizontal & vertical constraint for it.
Say: you has UIView child inside UIView parent. You pin left, top, bottom, right from child to parent. There are ENOUGH constraints, to calculate child's position and size - it's ok.
Now, you do have UIView child inside your UIScollView parent - and the same set of constraints. There are NOT ENOUGH constraints to calculate scroll view's content size. For calculating scroll view's content size, you should set for example equal widths & equal heights constraints for your child to your UIScrollView parent.
Despite you've given not enough info to find your problem out, i think, you most likely didn't set constraints to size/position your UIScrollView inside it's parent. After that - you probably didn't set one more explicit constraint in each direction inside the scroll view to calculate its content size.
I strongly suggest to read ray wenderlich's iOS 6 by tutorials autoalayout parts - you will understand HOW and WHY auto layout works so.
Say you are working with a nib whose size is 568 in height, and the inner view is 650 high.
First make the scroll view the same height as the inner view. Then pin the four sides of the scroll view to its container view, however you will notice that the bottom constraint is a negative number - make this 0.
You will notice that there is a problem with the bottom constraint:
Don't worry, that will be fixed in the next step.
Now pin all four sides of the inner view to that of the scroll view, as well as its width and height:
You will now notice that the previous error has disappeared, and when you run the project, you should now be able to scroll to see the content out of view.

How to make a ScrollView with AutoLayout in Xcode5

In xcode 5 using storyboards how would one make a fully operational vertical scrolling scrollview, with AutoLayout ON?
Considering the subviews have hierarchy:
1.UIView
2.UIScrollView
3.UIView (lets call this UIDetailView to make things easier)
Please be specific from code to constraints to wether any of the views HAS to be smaller etc.
UIScrollView with Autolayout within Storyboards Just Works
I've seen a number of people recommending the 'Container View' approach, AKA brute force, to solving the problem that they don't understand. It is non-optimal since you now have lost a big advantage of the scrollview by making it think the content is the entire scrollview rather than the subviews immediately attached to the scrollview.
Here is how I did it in the example that follows
--UIScrollView
|-> UITextView
|-> UILabel
|-> UIOtherStuff
When placing a UIScrollView into a UIView in a Storyboard just pin the edges to the 4 sides of the UIScrollView to the UIView. Now add your content to the UIScrollView making sure that you provide a minimum of two constraints for each dimension. The great thing about Autolayout is that it figures out how big the contentSize of the scrollview, or UILabels for that matter, needs to be based upon the size of the content inside it. AKA intrinsicContentSize. So if you are given a warning 'Ambiguous content size for scrollView' you know that you have not given the content enough constraints. For example, you might have given Top, Bottom, Left, Right spacing distance between views but the subview you're constraining needs a height too since an infinite vertical plane like this UIScrollView could assume your view was from zero to infinitely high.
To put it another way the Apple guide on Autolayout by Example makes a simple 3 point plan for success:
Create the scroll view.
Place the UI element inside it.
Create constraints that fully define the width and height of the scroll view content.
That top TextView with 'Min melding til' is also growing as you type more lines into it and the whole ScrollView grows to contain it. While I override the UITextView class to return a modified height constraint, the ScrollView itself works correctly without coding.
One last thing, lots of posts related to Autolayout try the magical fix-all incantation translatesAutoresizingMaskIntoConstraints = NO. This is only necessary if the view is created programmatically.
This blog post details how to use a UIScrollView with Autolayout ON, using a pure autolayout approach. Note though that all constraints in the blog post are defined through the Storyboard.
The approach in the post assumes the following hierarchy:
1. View (main view of my UIViewController)
2. Scroll View (UIScrollView)
3. Container View (UIView)
4. Content View (e.g. UIImageView)
I guess the Container View will be your UIDetailView, and the Content View will be any UIView inside your UIDetailView.
https://happyteamlabs.com/blog/ios-how-to-use-uiscrollview-with-auto-layout-pure-auto-layout/
The documentation clearly states how to do this:
A UIScrollView in auto-layout will always resize itself to fit the content (UIDetailView).
So you have to set up your views like this:
UIView: Position with constraints.
UIScrollView: Bind to UIView with constraints.
UIDetailView: Set size (intrinsic content size), max out compression-resistance, set top-, bottom-, leading- and trailing constraints to UIScrollView to 0 manually.
I had a similar problem and i found relative simple solution similar to DJ S's from within Interface Builder using pure Autolayout without any code.
For proof-of-concept at first remove any constraint in View Controller to if see this works.
This is sample layout:
View (main view of my UIViewController)
Scroll View (UIScrollView)
Container View (UIView)
Content View (e.g. UIImageView)
A. Scroll View width/height should be smaller that Container View width/height
B. Container View should have some determinated width/height (may be explicit width/height )
C. Do Control-drag Container View to Scroll View and add only:
Leading Space to Container
Trailing Space to Container
D. Check out those two constraints and set "constant" value for both to 0
E. Run app and
Because of the new iPhone 6 and 6+ screen sizes, I had to make a few tweaks to DJ S's solution.
The goal
Position a UITextView inside a UIScrollView, and also have 15 pt spaces from the left/right screen edges.
Views
1. Main View (main view of my UIViewController)
2. Scroll View (UIScrollView)
3. Container View (UIView)
4. Text View (UITextView)
Solution
For the spaces, I added 15 pt horizontal trailing/leading spaces from UIScrollView->Main View. To make the UITextView's width relative to the screen width, I added an Equal Widths constraint from UITextView -> Main View and set the value to -30 (2 * the 15 pt horizontal space). Now, the UITextView's width will dynamically adjust for any screen size.
The UIScrollView should have Scrolling Enabled. The UITextView should not.

Resources