Add UI elements to UIScrollView? - ios

I have a scroll view. On the scroll view I have added many necessary UILabels and UIButtons etcs.
Now I want to add MORE labels and buttons but I have no room on the interface of the view to add them, otherwise they will overlap the other elements. How do I add more UI elements so that they can be interacted with when the user scrolls? I was thinking if I hold the UIelement over the bottom of the scroll view, the scroll would "scroll down" and present me with more space.

The key thing here is to remember that a view controller's main view in a nib (.xib file or storyboard) is always resized anyway when it is put into the interface. The size you see it at in the nib editor (Interface Builder) is just a "serving suggestion". Thus, SamirChen's answer is quite right: set the view to Freeform so you can resize, and just make it big enough to hold the scroll view which itself is big enough so that you can put in all the desired contents.
I would add just two points that you will find helpful:
Use autolayout outside the scroll view. This will cause the scroll view to become the right size when the view is resized to fit the interface, and will cause everything else to be repositioned correctly.
Use autolayout inside the scroll view too! This is the really cool part. If you set up sufficient constraints between all the contents of the scroll view and the scroll view itself (their superview), the contentSize will be calculated for you automatically using constraints from the inside out! Thus, you don't have to know the content size or set it in code!
It is easier to see than to describe, so download the example code from my book and look at the examples currently entitled "bk2ch07p367scrollViewInNibAutolayout" and "bk2ch07p367scrollViewInNibAutolayout2".

In the Interface Builder, as far as I know, you cannot scroll the Scrollview.
If you just want to add more labels and buttons into your scrollview in the Interface Builder, you can set the size of the View Controller which holds your scrollview to be Freeform in Attribute Inspector. Then you can set the size of the View to any value you want in Size Inspector. After that, you can change your scrollview's size to add more labels and buttons.

"You could design a UIView with all this content on it in a .xib file and make the view whatever size you wanted, then put it into the scroll view in viewDidLoad" referenced from #nhgrif.
Don't forget to change the scroll view's contentSize property to meet the UIView's bounds.

Related

ScrollView is Not Appearing

Reference: Add a ScrollView to existing View
I inserted a scroll view into an existing view and now my page is not appearing and I am not sure how to fix this. My scroll view is under the view so I do not understand why it is not displaying.
With auto-layout, the UIScrollView needs to be able to calculate its content size using the available constraints. This is often best accomplished by adding a UIView in the scroll view to act as the content view, rather than directly embedding UIControl subclasses. The content view can then be constrained to be equal width and/or equal height to the parent view of the scroll view. The variable height/width (depending on the scroll direction) of the content view can be calculated by fully constraining the widgets it contains.

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.

XCode: laying out scrolling content in IB

TL;DR: What's the best way to layout over-sized scroll-view content in Interface Builder?
I am trying to figure out the least painful way to layout offscreen / scrolling content in Interface Builder. My current approach has been to tweak scrollview content width and height constraints to see all content in IB, before reverting to proper contraints for building, but this gets to be a hassle. That said, I would prefer to do more more graphically, and less programmatically for general ease of editing.
The general approaches that I can think of are:
use embedded segues to build up views in a size-accurate fashion (the most intuitive, flow-charty approach)
tweak constraints for editing, reset for building
use placeholder values / constraints in IB (haven't tried this yet)
layout over-sized content programmatically
In more detail, I am trying to build a vertically scrolling view comprised of sub views. Each sub view is self contained and can potentially appear in more than one context and I would like to keep them isolated (in fact, I am embedding them as well, but that does not affect the question). What I would like to do, is use IB to layout a tall composite content view. I would then like to create the outer scroll view with a single content view again of a container view, embedding the composite content view. Ie. the grey content view on the left embeds the taller red / blue content on the right, and I am wondering if I can deduce the height, so it could be device agnostic (the views are square, so they will take their width from the device)
So summarized, is there anyway that I could derive the hosting scrollview's contentView height from the actual height of the content of the embedded view, or should I instead use approaches 2, 3, or other?
To be clear, Auto Layout requires that scroll view content views define constraints for the edges, and width / height dimensions. In my case, I would like to be able to preset the width to the device width, but leave the height to be derived from the red/blue content view. IB won't let me drag constraints between the embedding and embedded scenes in Storyboard, and when I leave the field missing, it uses the prototype values for the content height.
Is it recommended to use placeholder sizes (3) in IB to make the content visible for editing, knowing that run-time constraints will render it properly (ie over-size scroll views in Storyboard so all scrolling content is visible)? Conversely, is it foolish to even try - should I do all scroll-view content composition programmatically (4)?
Thanks in advance.
Edit:
Placeholder constraints allow you to get by, but force you to change the priority of the run-time constraints, and trigger IB / layout errors and warnings for conflicting constraints, despite selecting the placeholder checkbox on conflicting design-time-only constraints.
Use a UITableviewController with static cells. The cells will be your content views.
You can scroll the tableview down in IB to see the cells at the bottom and you get a nice extra behaviour like scrolling the views to the correct position when the keyboard is opened so it doesn't hide the textfield you clicked on.
And you don't have to worry about the scroll view at all.
If you want to reuse your content views you can make the table dynamic of course and provide the content views by code.

iOS - Change size of view in Interface Builder for putting into scrollview

Here is my problem:
I want to create a view whose height exceeds the height of the iPhone-screen. To deal with that, I am using a UIScrollView which works just fine. I created the scroll view in interface builder and set the content size programmatically to twice the height of the screen (that's enough for my purpose). I can run the code and it works just fine. I can scroll up an down without trouble.
The only thing I don't get is how I can add more items (buttons, labels etc.) to the view in interface builder, since it doesn't allow me to change the size of the view. I was thinking to create a scroll view as a top level object and then place a view (the contentview) of a larger height into it. Now the only thing to do is add all the items to the contentview, which is not possible because in interface builder the contentview has only its regular size.
I am thankful for any thoughts on this :-)
You can change the size of your UIView from the interface builder.
For that you have to go to Interface Builder of particular UIView. From Simulated Metrics *select Size = Freeform* and now you are able to change the size of UIView from interface builder.
The accepted answer is accurate but no longer complete for iOS7 and XCode5. Check out #Whasssaaahhh answer here for more detail.
You can drag items on top of the view in two ways.
1) Change Y-Position: If you select your UIView and in Size Inspector change its Y position to whatever section of the view you want to see (try something like -100 to go down or 100 to go up, play around with whatever numbers work for your screen size) you can drag all your buttons, labels, text fields however you like for each visible section.
2) Drag With Mouse: If you carefully select just the UIView with your mouse and carefully drag it either upwards or downwards and let go, it will snap to the new position so you can edit it. It's easier to "see" this if you drag some objects on it first.
You can change the size of your view and size of your scrollview along with the addition of objects.
add the added object height to the scrollView.contentSize and also add into the height of the view.

how do I use UIScrollView in Interface Builder?

While I've used UIScrollView successfully in the past by manipulating it programmatically, I'm having trouble getting it to work by setting it up exclusively in Interface Builder.
I have a simple "about" page in my iPhone app. It has a UITextView, some icons, and links to my other apps. I have added all of these views to my UIScrollView, arranging them so that their total size is > 480. When I launch my app, the scrollview displays only the contents that fit on the screen, and nothing scrolls.
Is it possible to do this entirely via IB, or must I manipulate the contentSize via code?
You forgot to set the contentSize property of the UIScrollView. Strangely enough you can not do this from Interface Builder. You will have to do it from the view controller managing this scroll view.
Boby_Wan's answer got me thinking, and I found the following solution to configure the UIScrollView's contentSize from Interface Builder:
Select the UIScrollView in the Storyboard scene
Go to the Identity inspector, create a new User Defined Runtime Attribute (click the + button)
Change the attribute Key Path to contentSize
Change the attribute Type to Size
Now set the Value to {desired content width, desired content height}
eg setting the value to {320, 920} will let the user scroll down a whole extra screen on the iPhone.
(I am using xcode 4.3.3, the project's iOS Deployment Target is 5.1)
When I first did this I received the following error:
Illegal Configuration:
Size type user defined runtime attributes with Xcode versions prior to 4.3
MainStoryboard.storyboard
If you too get this error it is simple to fix: select the Storyboard in the Project Navigator, and bring up the File inspector. Find/expand the Interface Builder Document section, and there is a drop down for Development. Ensure this is set to Xcode 4.3
With Autolayout (iOS6+), you can avoid setting contentSize. Set the following constraints instead:
Pin the top of the scrollview to the top of its top-most child.
And pin the bottom to the bottom of its bottom-most child.
You can do it using only Interface Builder, go to the Identity Inspector (the third inspector tab) and add a new User Defined Runtime attribute with
Key Path: contentSize
Type: Size
Value: {width, height}
Now there is a way to make a UIScrollView scroll without leaving Storyboard:
Select the UIScrollView in the Storyboard, go to the Size
inspector and change the Bottom value (or whatever other value
you need to change) in the Content Insets section to the height of the content area.
Now go to the Identity inspector and create a new User Defined Runtime Attribute (by clicking the + button) and name it contentSize. It doesn't matter what Type or Value you fill in (you can even leave their default value).
This will make the UIScrollView work properly, although I don't know why the second step is necessary (I found out by chance). :(
one approach i have used in the past is to drag the scrollview out of it's containing view in interface builder, and set it's actual size to what want the contentSize to be.
what is not inherently obvious about interface builder is you can have unassociated views that are stored in the nib, but aren't a part of the main view the nib is primarily for.
in the view where you want it the scrollview to live, place a simple UIView, which you use as a place holder. (this is simply so you can visually design it's location. if you are just using the entire view, you can skip this step and use the second code snippet i supply at the end of this answer).
you can then populate the scrollview with controls, visually laying it out how you want it to be. give both the placeholder and the scrollview properties inside your view controller so you an access them at runtime.
at runtime, in - (void)viewDidLoad
scrollView.contentSize = scrollView.frame.size;
scrollView.frame = placeholder.frame;
[placeholder.superview addSubView:scrollView];
[placeholder removeFromSuperview];
alternatively (if you didn't use a placeholder):
CGRect f = self.view.frame;
scrollView.contentSize = f.size;
f.origin.x = 0;
f.origin.y = 0;
scrollView.frame = f;
[self.view addSubView:scrollView];
finally, if you "lose" your scroll view in interface builder (it's possible to close it so it disappears from the design grid), don't panic. just click on it in the object list to the left of the design grid.
In Xcode 4.5 using Autolayout I have no Content Insets section in my size inspector. So I had to add it under User Defined Runtime Attributes and then it worked fine.
What you add in "User Defined Runtime Attributes" is keyPath == contentInset which is of type "Rect" (UIEdgeInsets, which has the same input as a Rect) and is defined as {top, left},{bottom, right}. The contentSize only defines the region of the scrollview window. contentInset defines the scrollable area.
I hope this helps somebody in the same situation.
Many of the answers above are misleading or outdated. As of 2017 (possibly much earlier) interface builder does support scrollviews with automatically sized content. The trick is that XCode gives a special, non-standard meaning to the constraints between the scrollview and the content inside it. These "inward" constraints will not actually affect the size of the content as you might otherwise expect.
This means that you can e.g. have your scrollview pinned to the bottom of the main view with zero margin, and also have your scrollview's content pinned to the bottom of the scrollview with zero margin, but the content will not actually be stretched by this. Instead the content will get its self-determined size (more below) and this will also be the size of the scrollable area within the scrollview.
Think of it like this - There is an asymmetry in binding constraints to a scrollview: Constraints from the scrollview to the "outside" (parent) world determine the size and position of the scrollview as usual. But constraints "inside" the scrollview are really setting the size and position of the scrollable area of the scrollview, by binding it to the content.
This is totally non-obvious because when you go to set the constraints XCode will always suggest the current spacing and it might never occur to you to intentionally change the inward and outward facing constraints in a way that conflicts. But you can and they have the meaning described above: one controls the scrollview layout and one controls the scrollable content area size.
I stumbled upon this by accident and then seeing how it appeared to work lead me to this article that explains it completely and cites the Apple docs source for this:
https://spin.atomicobject.com/2014/03/05/uiscrollview-autolayout-ios/
One last critical piece of information, about the content's self-determined size: You may feel that you are in a catch-22 here because you normally size your content to e.g. the parent view's width, but in this case the parent is the scrollview and as described above - the constraint will not affect your content size. The answer here is to remember that you can constrain items to items not directly neighboring in your view hierarchy: e.g. You can set the width of your content view to the width of the main view instead of trying in vain to get the scrollview to do it for you.
If you click on the Properties icon of any View Controller in Interface Builder, you can set it to a "Freeform" size in Simulated Metrics and change the size of the main View to be your desired content size.
This way you can create your ScrollView's content as if it were one large view. As it's only a simulated metric your View Controller will be resized to the window's bounds when it's loaded.
Setting up a UIScrollView via Interface Builder is not intuitive. Here is my checklist for setting it up:
Select the UIViewController's XIB file. In the interface builder's "Identity Inspector", change the UIView to class type UIScrollView
Under "File Inspector", uncheck Autolayout
Under "Attributes Inspector", change the size to Freeform. You can then stretch the Scroll View manually or you can specify a custom width and height under "Size Inspector".
In "Identity Inspector", add a new User Defined Runtime Attribute called "contentSize" of type "Size" and change it to a value like {320, 1000}. You cannot set this programmatically anymore and therefore need this step so that Scroll View knows that contents of the Scroll View are bigger than the window.
Just remove the autoLayout on your scrollview. then the code is as simple as this:
scrollviewName.contentSize = CGSizeMake(0, 650);
just create an iboulet property on .h file then synthesize on .m file. Make sure that the scrolling is enabled.
Yes, st3fan is right, UIScrollView's contentSize property must be set.
But you should not turn off autolayout for this purpose.
You easily can setup contentSize of UIScrollView with autolayout in IB only, without any code.
It is important to understand that when using autolayout contentSize of UIScrollView is not set directly, it is calculated based on constraints of all subviews of UIScrollView. And all you need is to provide proper constraints for subviews for both directions.
E.g. if you have only one subview you can set its height and space from top and bottom to superview (i.e. scrollView in our case) contentSize.height is calculated as sum
Vertical Space (aSubview.top to Superview.top) + aSubview.height + Vertical Space (aSubview.top to Superview.top)
contentSize.width is calculated similarly from horizontal constraints.
If there too few constraints to calculate contentSize properly small red button is shown near View Controller Scene item to inform about layout ambiguity.
If there are many subviews then it may be "chain" of constraints: top to topmost subview, heights and spaces between subviews and bottommost subview to bottom like in Danyal Aytekin answer.
But in practice in most cases it is more convenient just to add a empty view with required size and set spaces to top, left, bottom, right of scrollView to 0.
You can use this view as "content View" i.e. put all other subviews on it or if you already have many subviews and do not want move them and setup layout again you can add this auxiliary view to existing subviews and made it hidden.
To make scrollView scrollable calculated contentSize must be greater than scrollView size.
You can have UIScrollView in StoryBoard with Autolayouts.
Basically what do you need is:
Add UIScrollView
Add all constraints to it (like insets from top, left, right, bottom edges)
Add a 'container' UIView to UIScrollView
If you want just one-direction scroll (say, vertical):
set height explicitly (for the instance, 600) and link width to the width of UIScrollView.
If you want two-directional scroll just set both width and height.
Here's a solution to design ScrollView with a content larger than the screen entirely in Storyboard (well, almost entirely, you'll need to add 1 single line of code too)
https://stackoverflow.com/a/19476991/1869369
I find out a more convenient way.
1: Scale the size of the scroll view to contain all the ui inside it.
2: Add a iboutlet of the scroll view.
3: In viewDidLoad, save the frame.size of the scroll view.
(e.g. _scrollSize = _scrollView.frame.size;)
4: In viewWillAppear, set the contentSize by the CGSize you saved before.
(e.g. _scrollView.contentSize = _scrollSize;)
5: Done~
Add UIViewController
In UIViewController 'Attribute Inspector -> Simulated Metrics' set size Freeform
In UIViewController 'Size Inspector -> View Controller' set height 800
Add UIScrollView in UIViewController
Add all constraints to UIScrollView (top, left, right, bottom) and add alignment X = center
Add UIView in UIScrollView
Add all constraints to UIView (top, left, right, bottom) and add alignment X = center. Yes, same as for UIScrollView in #3, but for UIView
Add height constraint to UIView. For example 780
Run
Create a new Xcode Project
Navigate to Main.storyboard file
Select ScrollView from the objects library.
Set frame for the ScrollView.
Add another view to scroll view and keep the frame same as that of ScrollView.
Now to set its height and width dynamically you may this Configure A UIScrollView Using Auto Layout In XIB
You can do it entirely in Interface Builder without setting the "contentSize" property in code.
Put only one View in Scroll View. The Scroll View should only has this child view. You can name it as Content View. And put all contents inside this View later.
Align four edges of this View to the Scroll View (the View's superview) with zeros.
Set the width and height of this View. The width and the height will be implicitly treated as the "contentSize" of the Scroll View.
Simply put, the width and height of this View define the "contentSize" of the Scroll View. Because this View is the only content of the Scroll View, the size of this View is the content size of the Scroll View. It is quite reasonable.
I learned it from this tutorial:
https://riptutorial.com/ios/example/12812/scrolling-content-with-auto-layout-enabled

Resources