drawing a uiview offscreen - ios

I want create a UIView that's offscreen when ViewDidLoad is called, but that I will animate up onto the screen once a certain function has been called. The code for animating the UIView is fine, but I can't seem to draw the UIView offscreen to begin with (I've assembled the UIView in storyboard onto my UIViewControleller). This is the code I've tried:
- (void)viewDidLoad {
...
[_tagView setFrame:CGRectMake(0, self.view.frame.size.height+40, self.view.frame.size.width, 40)];
...
}
_tagView is drawn where I've placed it in the StoryBoard editor, which is at the bottom of my UIViewController.

A couple of things: A view object's frame gives it's coordinates in it's parent view's coordinate space.
A view's bounds are it's internal coordinate system.
When you set a view's frame, it should be expressed in terms of it's parent's bounds, not it's parent's frame.
So your code should be creating your _tagView's frame in terms of self.view.bounds, not self.view.frame.
Next, are you using AutoLayout or struts and springs? In Xcode 5 everything defaults to AutoLayout. You can't change frames and bounds when AutoLayout is active. Instead you have to change the relevant constraints. If you want to set a view's frame manually, you probably want to turn off AutoLayout and use "struts and springs" style view geometry.
EDITED: I moved some of the info from comments up into my original answer for clarity:
If you want to move a view at runtime with Auto Layout active, you have to find the constraint for the setting you want to change, (width, height, leading edge, trailing edge, etc.) and control-drag from the constraint into your .h file to make an outlet. Then in your code you change the constant property associated with the constraint. – Duncan C 22 mins ago
Note that you CAN turn Auto Layout off on a storyboard-by-storyboard or NIB-by-NIB basis. If you select the file and choose the File Inspector, there is a checkbox "Use Autolayout" hidden in the "Interface Builder Document" section. I always have trouble finding it.
Here is a screen-shot showing the location of the button in the Xcode project window (far right side)

Related

Aligning custom view in Main Storyboard

I have created a custom view in .xib using IB. Here is the structure and relative constraints I have created:
The layout of custom view is like this:
I have set the File's Owner to Player.class. So in Main.Storyboard I have added a view and changed it's class name to Player. For it's constraints, I have put the leading and trailing equal to Superview with constant 0; so it can stretches the width according to the screen. Now during design time, this is how the Player is appearing in IB:
See how it's appearing out of the view. During runtime also the placement of player controls are out of screen.
How to fix this layout to make it appear like in the actual xib layout, making the Slider stretches the width of screen and Player to appear in center?
It's actually my bad. I have to put the following code to make it's alignment according to bounds instead of frame, otherwise it will be offset.
view.frame = bounds
It then display it perfectly both during IB and at runtime.

Remove one view's layout constraints but keep its subviews'?

Working on a project in iOS 8 using storyboard and auto layout:
In storyboard, specified constraints for this view and its subviews
In code, in response to touch events, I'm going to change this view's size by setting its frame
To make both 1 and 2 warning free, I'm doing the following when first changing its size with code:
[theView removeConstraints:theView.constraints];
theView.translatesAutoresizingMaskIntoConstraints = YES;
theView.frame = CGRectMake(0,0,width,height);
If not doing the first line, Xcode will complain a whole bunch about constraint conflicts, however adding this line will remove all its subview's constraints as well. So my question is: is there way to just remove this uiview's constraints but not its subview, say a button on it still wants to center its self relative to this view's size and position?
First, you don't need to resize the view by setting frame otherwise what is the point of keeping the constraint at first place. You could have position that by simply having a correct initial frame.
Secondly, you have a mis-conception about "TO-WHOM" a constraint has been applied to.
say a button on it still wants to center its self relative to this view's size and position?
When you apply a position related constraint to a view you normally apply it to it's superview. Means if you want to position a subview in the horizontal centre of a view then the constraint is added on view not on the subview. That's why when you called a removeContraints: message on view that position constraint was removed and now your subview isn't bound to any constraint. However the width and height constraints are applied to subviews itself.
To solve this you need to make IBOutlets for constraint that you need to modify, which in your case should be width, height, horizontal x and top constraint; and then change the constant values for them respectively.
theView.widthConstraint.constant = newValue;

iOS autolayout-move a view located inside a tableviewcell to the center of the screen

I have a tableview with cells containing text views as well as imageviews. My project is currently using AutoLayout. My goal is to get the imageview to display in fullscreen when it is tapped. One option is to use a modal view controller, but I want to have this work sort of like the way tapping on images in the facebook app works, the app centers the image and fades the background.
Since I'm using autolayout, I cannot simply set the frame of the imageview to fill the screen. Instead, I need to use autolayout constraints. My image view has 5 constraints, a constraint setting a distance from the bottom of the cell, as well as the left an right sides, and one controlling the image height. The last is a vertical space constraint between the textview above the image view and the top of the image. While this would appear to conflict with the height and bottom constraints, for some reason interface builder forces me to have this. To avoid problems, I set this constraint's priority to be less than 1000 (the image should never overlap the textview anyways, since the tableview cell height is set so everything will fit perfectly).
To center the image, I set the distance from the left and right to be zero and remove the vertical space constraint. In order to center the image, I replace the bottom space constraint with a center y alignment constraint to the UIWindow as opposed to the tableviewcell. I want to have it be in the center of the screen, not the cell.
To get the main window I use this:
AppDelegate* myDelegate = (((AppDelegate*) [UIApplication sharedApplication].delegate));
//access main window using myDelegate.window
Then, to set the constraint:
//currently sets the distance from the bottom of the cell to 14
//changing it...
[cellselected removeConstraint:cellselected.imagebottomspace];
cellselected.imagebottomspace = [NSLayoutConstraint constraintWithItem:cellselected.viewimage attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:myDelegate.window attribute:NSLayoutAttributeCenterY multiplier:0 constant:0];
[cellselected addConstraint:cellselected.imagebottomspace];
However, this doesn't work. The changes in the width and height of the image view apply just fine. However, when readding the imagebottomspace constraint, I get an unsatisfiable layout--apparently the constraint conflicts with another constraint which sets the distance between the bottom and the image view to 14, the very constraint I just removed. So it seems that it isn't actually removing the constraint.
When I proceed and let the app break a constraint, the imageview moves, but to the wrong place. It isn't centering in the screen. It moves way up and off the screen.
Obviously what I'm doing isn't right. What am I doing wrong?
So I guess you want something like this:
First, you need to know that as of Xcode 4.6.3, the nib editor (“Interface Builder”) has a bug when setting up constraints in a table view cell. It should create the constraints between the subviews and the cell's content view, but instead it creates the constraints between the subviews and the cell itself. This tends to screw up layout at runtime. (This bug is fixed in Xcode 5 and later.)
The consequence of this is that you should either remove all of the constraints that were in the nib and recreate them in code, or just get rid of the nib and create the cell's entire view hierarchy in code.
Second, there's an easier way to do the image zooming. Here's the basic procedure when a cell is selected:
Convert the selected cell's image view bounds to a CGRect in the top-level view's coordinate system.
Create a new image view just for zooming and set its frame to that CGRect. Set its userInteractionEnabled to YES. Set its autoresizingMask to flexible width and height. Add a tap gesture recognizer.
Add the new image view as a subview of the top-level view.
Set the cell's image view's hidden property to YES.
In an animation block, set the new image view's frame to the top-level view's bounds.
Disable the table view's panGestureRecognizer.
When the new image view is tapped, reverse the procedure:
Convert the selected cell's image view bounds to a CGRect in the top-level view's coordinate system.
In an animation block, set the zoomed image view's frame to that CGRect.
In the animation completion block:
Remove the zoomed image view from its superview.
Set the cell's image view's hidden property to NO.
Enable the table view's panGestureRecognizer.
Since you're not moving the original image view, you don't have to mess with its constraints. Hidden views still participate in layout.
Since you're creating the new image view in code, it will have translatesAutoresizingMaskIntoConstraints set to YES by default. This means that you can just set its frame. Auto layout will automatically turn the frame into constraints.
You can find the full source code in this github repository.
I've just come across a similar issue. I think that the reason for these problems are that the views embedded in UIScrollViews exist in a different bounds system to those of the views outside it. This is effectively how scrolling works in the first place, think of it as just applying a variable offset to the views it contains. Autolayout doesn't know how to translate between these different coordinate systems so any constraints that bridge across aren't going to be applied the way you expect.
To quote from Erica Sadun's excellent book iOS Auto Layout Demystified (from the section 'Constraints, Hierarchies, and Bounds Systems'):
"Be aware of bounds systems. You should not relate a button on some
view, for example, with a text field inside a separate collection
view. If there's some sort of content view with its own bounds system
(such as collection views, scroll views, and table views), don’t hop
out of that to an entirely different bounds system in another view."

How to use UIScrollView in Storyboard

I have a scroll view with content that is 1000px tall and would like to be able to lay it out for easy design on the storyboard.
I know it can be done programmatically but I really want to be able to see it visually. Every time I put a scroll view on a view controller it won't scroll. Is it possible to get it to work like I want or do I have to do it in the code?
I'm answering my own question because I just spent 2 hours to find the solution and StackOverflow allows this QA style.
Start to finish here is how to make it work in storyboard.
1: go to you view controller and click on Attribute Inspector.
2: change Size to Freeform instead of Inferred.
3: Go to the main view on that storyboard, not your scrollview but rather the top level view.
4: Click Size Inspector and set this view to your desired size. I changed my height to 1000.
Now you will see that you storyboard has your view setup so you can see the entire height of your scroll for easy design.
5: Drop on a scrollview and stretch it so it takes up the whole view. You should now have a scrollview with size of 320,1000 sitting on a view in your view controller.
Now we need to make it scroll and need to make it show content correctly.
6: Click on your scrollview and click on Identity Inspector.
7: Add a User Defined runtime attribute with KeyPath of contentSize then type of SIZE and put in your content size. For me it is (320, 1000).
Since we want to see our whole scroll view on the storyboard we stretched it and it has a frame of 320,1000 but in order for this to work in our app we need to change the frame down to what the visible scrollview will be.
8: Add a runtime attribute with KeyPath frame with Type RECT and 0,0,320,416.
Now when we run our app we will have a visible scrollview has a frame of 0,0,320, 416 and can scroll down to 1000. We are able to layout our subviews and images and whatnot in Storyboard just the way we want them to appear. Then our runtime attributes make sure to display it properly. All of this without 1 line of code.
Here are the steps with Auto Layout that worked for me on XCode 8.2.1.
Select Size Inspector of View Controller, and change Simulated Size to Freeform with height 1000 instead of Fixed.
Rename the view of View Controller as RootView.
Drag a Scroll View as subview of RootView and rename it as ScrollView.
Add constraints for ScrollView:
ScrollView[Top, Bottom, Leading, Trailing] = RootView[Top, Bottom, Leading, Trailing]
Drag a Vertical Stack View as subview of ScrollView and rename it as ContentView.
Add constraints for ContentView:
ContentView.height = 1000
ContentView[Top, Bottom, Leading, Trailing, Width] = ScrollView[Top, Bottom, Leading, Trailing, Width]
Select Attributes Inspector of ContentView, and change Distribution to Fill Equally instead of Fill.
Drag a View as subview of ContentView and rename it as RedView.
Set Red as the background of RedView.
Drag a View as subview of ContentView and rename it as BlueView.
Set Blue as the background of BlueView.
Select RootView, and click Update Frames button.
Update Frames is a new button in Xcode8, instead of Resolve Auto Layout Issues button. It looks like a refresh button, located in the control bar below the Storyboard:
View hierarchy:
RootView
ScrollView
ContentView
RedView
BlueView
View Controller Scene (Height: 1000):
Run on iPhone7 (Height: 1334 / 2):
Here are the steps that worked for me on iOS 7 and XCode 5.
Drag a ViewController (it comes with UIView "View").
1.1 Select "View Controller" and select "File Inspector" and uncheck "Auto layout".
Drag a ScrollView (as child of ViewController's UIView "View")
Select ScrollView and open "Identity Inspector".
Enter "contentSize" for keyPath. Select "Size" for Type. And Enter {320, 1000} for value.
Note: Step 4 is simply saying that the scroller contains some content whose size is 320x1000 units. So setting contentSize will make scroller work.
Select View Controller, Select "Attributes Inspector" then select Freeform from Size.
Note: step 5 will allow us to change the size of "View" that the view controller comes with.
Select "View" and then select "Size Inspector".
Set Width to 320 and height to 1000.
Note: 5, 6 & 7 is purely for us to see stretched or entire expanded view inside StoryBoard.
Note: Make sure to unselect "Auto Layout" on View Controller.
Your View hierarchy should look like:
After hours of trial and error, I've found a very easy way to put contents into scrollviews that are 'offscreen'. Tested with XCode 5 & iOS 7. You can do this almost entirely in Storyboard, using 2 small tricks/workarounds :
Drag a viewcontroller onto your storyboard.
Drag a scrollView on this viewController, for the demo you can leave its size default,
covering the entire screen.
Now comes trick 1 : before adding any element to the scrollView, drag in a regular 'view' (This view will be made larger than the screen, and will contain all the sub elements like buttons, labels, ...let's call it the 'enclosing view').
Let this enclosing view's Y size in the size inspector to for example 800.
Drop in a label onto the enclosing view, somewhere at Y position 200, name it 'label 1'.
Trick 2 : make sure the enclosing view is selected (not the scrollView !), and set its Y position to for example -250, so you can add an item that is 'outside' the screen
Drop in a label, somewhere at the bottom of the screen, name it 'label 2'. This label is actually 'off screen'.
Reset the Y position of the enclosing view to 0, you'll no longer see label 2, as it was positioned off screen.
So far for the storyboard work, now you need to add a single line of code to the viewController's 'viewDidLoad' method to set the scrollViews contents so it contains the entire 'enclosing view'. I didn't find a way to do this in Storyboard:
- (void)viewDidLoad
{
[super viewDidLoad];
self.scrollView.contentSize = CGSizeMake(320, 800);
}
You can try doing this by adding a contentSize keyPath as a size to the scrollView in the Identity Inspector and setting it to (320, 1000).
I think Apple should make this easier in storyboard, in a TableViewController you can just scroll offscreen in Storyboard (just add 20 cells, and you'll see you can simply scroll), this should be possible with a ScrollViewController too.
Getting Scrolling to work in iOS7 and Auto-layout in iOS 7 and XCode 5.
In addition to this: https://stackoverflow.com/a/22489795/1553014
Apparently, all we need to do is:
Set all constraints to Scroll View (i.e. fix scroll view first)
Then set distance-from-scrollView constraint to the bottom most item to scroll view (which is the super view).
Note: Step 2 will tell storyboard where the last piece of content lies within Scroll view.
For this example, I have unchecked the Autolayout feature of the Interface builder. And, I'm still using (for no reason at all) the relatively old 4.6.1 version of Xcode.
Start with a view controller that has a scroll view over it (the main view).
1: Add a Container View, from the Object Library, to the scroll view. Notice that a new view controller is added to the storyboard and it is linked to the view controller with the scroll view.
2: Select the container view and, on the Size Inspector, make it anchor to top and left without auto resizing.
3: Change its height to 1000. (1000 is used for this example. You should apply the value that you require.)
4: Select the new view controller and, from the Attributes Inspector, change Size to Freeform.
5: Select the view of the new view controller and, on the size Inspector, change the height to 1000 (which is equal to the container view's height).
6: For your test later, while still on the view of the new view controller, add a label at the top and at the bottom of the view.
7: Select the scroll view from the original view controller. On the Identity inspector, add an attribute with the keyPath set to contentSize, type set to Size, and value set to {320, 1000} (or your container view's size).
8: Run on the 4-inch iPhone Simulator. You should be able to scroll from the top label up to the bottom label.
9: Run on the 3.5-inch iPhone Simulator. You should be able to scroll from the top label up to the bottom label.
Remember that Xcode 4.6.1 can only build for iOS6 and below. Using this approach and building for iOS6, I am still able to achieve the same results when the app is run on iOS7.
Note that within a UITableView, you can actually scroll the tableview by selecting a cell or an element in it and scrolling up or down with your trackpad.
For a UIScrollView, I like Alex's suggestion, but I would recommend temporarily changing the view controller to freeform, increasing the root view's height, building your UI (steps 1-5), and then changing it back to the standard inferred size when you are done so that you don't have to hard code content sizes in as runtime attributes. If you do that you are opening yourself up to a lot of maintenance issues trying to support both 3.5" and 4" devices, as well as the possibility of increased screen resolutions in the future.
Disclaimer :- Only for ios 9 and above (Stack View).
If you are deploying your app on ios 9 devices use a stack view.
Here are the steps :-
Add a scroll view with constraints - pin to left, right, bottom, top (without margins) to superview (view)
Add a stack view with same constraints to scroll view.
Stack View Other Constraints :- stackView.bottom = view.bottom and stackView.width = scrollView.width
Start adding your views. The scroll view will decide to scroll based on the size of the stack view (which is essentially your content view)
Here's how to setup a scrollview using Xcode 11
1 - Add scrollview and set top,bottom,leading and trailing constraints
2 - Add a Content View to the scrollview, drag a connection to the Content Layout Guide and select Leading, Top, Bottom and Trailing. Make sure to set its' values to 0 or the constants you want.
3 - Drag from the Content View to the Frame Layout Guide and select Equal Widths
4 - Set a height constraint constant to the Content View
i wanna put my 5 cents to accepted answer:
i've been researching topic for 2 days and finally found a solution that i will be using always from now on
go up to item 4 in accepted answer and forget about adding attributes of frames and contentsizes and so on
to make everything automatic just use solution from this link
everything is clear, easy, elegant and works like a charm on ios 7. i'm pretty glad with all that lol
You should only set the contentSize property on the viewDidAppear, like this sample:
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
self.scrollView.contentSize=CGSizeMake(306,400.0);
}
It solve the autolayout problems, and works fine on iOS7.
Here is a simple solution.
Set the size attribute of your view controller in the storyboard to "Freeform" and set the size you want. Make sure it's big enough to fit the full content of your scroll view.
Add your scroll view and set the constraints as you normally would. i.e. if you wants the scroll view to be the size of your view, then attach your top, bottom, leading, trailing margins to the superview as you normally would.
Now just make sure there are constraints in the subviews of the scrollview that connect the top and bottom of the scroll view. Same for left and right if you have horizontal scrolling.
In iOS7 I found that if I had a View inside a UIScrollView on a FreeForm-sized ViewController it would not scroll in the app, no matter what I did. I played around and found the following seemed to work, which uses no FreeForms:
Insert a UIScrollView inside the main View of a ViewController
Set the Autolayout constraints on the ScrollView as appropriate. For me I used 0 to Top
Layout guide and 0 to Bottom layout Guide
Inside the ScrollView, place a Container View. Set its height to whatever you want (e.g. 1000)
Add a Height constraint (1000) to the Container so it doesn't resize. The bottom will be past the end of the form.
Add the line [self.scrollView setContentSize:CGSizeMake(320, 1000)]; to the ViewController that contains the scrollView (which you've hooked up as a IBOutlet)
The ViewController (automatically added) that is associated with the Container will have the desired height (1000) in Interface Builder and will also scroll properly in the original view controller. You can now use the container's ViewController to layout your controls.
Here's a bit of a grubby answer that get's to the same solution for vertical scroll views, but (against the ethos of stackoverflow) doesn't answer the question. Instead of using a scrollView, just use a UITableView, drag a normal UIView into the header, and make it as big as you want, you can now scroll the content in storyboard.
Apparently you don't need to specify height at all! Which is great if it changes for some reason (you resize components or change font sizes).
I just followed this tutorial and everything worked: http://natashatherobot.com/ios-autolayout-scrollview/
(Side note: There is no need to implement viewDidLayoutSubviews unless you want to center the view, so the list of steps is even shorter).
Hope that helps!
The key is the contentSize.
This is often missing and not indicated when adding a UIScrollView.
Select the UIScrollView and select the Identity Inspector.
Add a contentSize keyPath as a size to the scrollView in the Identity Inspector and setting it to (320, 1000).
Scroll away.
If you are using auto-layout than best approach is to use UITableViewController with static cells in storyboard.
I have also once faced the problem with a view that require much more scrolling so change the UIScrollView with above mentioned technique.

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