Strange behavior when setting height of a table header view - ios

I am trying to make my table header equally high as its contents. It seems impossible to do this in storyboard so I tried to programmatically set the height upon loading the view. Yet the table header is much higher than its subviews while the frame size is said to be the same.
I did this in my viewDidLoad function.
override func viewDidLoad() {
super.viewDidLoad()
ongoingOrderTableView.backgroundColor = UIColor.blueColor()
tableHeader.backgroundColor = UIColor.redColor()
tableHeader.frame.size.height = 440
print(tableHeader.frame.size)
//(560.0, 440.0)
print(qrLabel.frame.size)
//(54.0, 21.0)
print(qrImage.frame.size)
//(440.0, 440.0)
}
Printed result showed that the settings are correct.
The UI on simulator is as below:
Can somebody explain why did this happened and how to fix that?

When using auto layout, iOS will keep a 600 * 600 screen size until func viewDidLayoutSubviews() is called. Hence you should do your dynamic adjustment of layouts in viewDidLayoutSubviews() rather than viewDidLoad().
I put the print(tableHeader.frame.size) in viewDidLayoutSubviews() as well and the result is (374.0, 501.0). And when I adjust header height in this function it all turned out correct.

Set the height of the table header view in viewDidLayoutSubviews().
To debug the issue , add a breakpoint in viewDidLayoutSubviews() and check the header height.
In my experience this happens in case you dont add the tableHeaderView from storyboard.

This is the issue with your navigation bar,
Try switching your top bar to Opaque Navigation Bar, Select NavigationController on your storyboard > Simulated Metrics > Top Bar > Opaque Navigation Bar
For example having image view :
Default/ Transclucent :
Opaque :

Related

UIScrollView for a smaller UIView inside main UIView

I'm having trouble getting this working, best explained what i am trying to do with this image of my storyboard.
The main goal is to make that container scrollable, with its size dependent on the view controller that gets added into it via code.
Here is how i have laid out my views.
Obviously my constraints are what is messing me up, but not exactly sure how to get them to behave as expected, I've tried pinning the container to the scroll view and then the scroll view to the main view. The screen appears how i want but it just doesn't scroll.
Any help is much appreciated!
It's impossible from your question & screenshots to determine what constraints you actually do have set up. And your question doesn't even particularly make it clear whether your constraints are behaving properly (aside from the scroll view not scrolling, which may or may not be related to constraints).
First, we need to make sure our constraints are hooked up correctly. The container view for which you're going to put a scroll view into should have four constraints. One for each side, left & right, pinning it to those edges. One for the bottom to pin it to the toolbar, and one for the top to pin it to your top views. It doesn't need any more constraints. Now, the toolbar at the bottom and your views at the top need to have their constraints set up so that they have a constant height per device/orientation, and does not care about the size of the container view. If your constraints are set up correctly, then resizing this view in your interface builder file should be changing the size of the container view. If this isn't happening, head back to the drawing board because something isn't quite right.
If you are confident your constraints are set up correctly, there are a few other things about the scrollview itself that could prevent scrolling.
First, check the most obvious. Is the scrolling enabled property set to true on your scroll view? If not... of course it's not going to scroll.
The other thing that could be happening is that your content view is not larger than the scroll view. Make sure that you've properly set up autolayout for the contents of the scroll view, otherwise the content view will not be larger than the scroll view, and no scrolling will be happening.
So for anyone wondering how to do this, i finally figured out how to do it after a lot of trial and error.
Basically what I did was remove the container view from the storyboard, set my constraints on the scroll view as i normally would:
and then added a container view as a subview of the scroll view in code:
var containerView : UIView!
var currentViewController: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
self.containerView = UIView()
self.scrollView.addSubview(self.containerView)
}
To swap between different view controllers using my UISegmentControl and have the scroll view scroll the content no matter how large the new view controller is i added the following code in my UISegmentControl functions
#IBAction func segControlValueChanged(sender: AnyObject) {
if let vc = viewControllerForSelectedSegmentIndex(sender.selectedSegmentIndex) {
self.currentViewController!.view.removeFromSuperview()
self.currentViewController!.removeFromParentViewController()
displayCurrentTab(sender.selectedSegmentIndex)
}
}
func displayCurrentTab(tabIndex: Int){
if let vc = viewControllerForSelectedSegmentIndex(tabIndex) {
self.addChildViewController(vc)
self.containerView.addSubview(vc.view)
vc.didMoveToParentViewController(self)
vc.view.frame.size = CGSize(width: self.view.frame.width, height: vc.view.frame.height) //had to add this because otherwise for some reason my new vc width would not increase or decrease to its parent view
self.currentViewController = vc
self.scrollView.contentSize = vc.view.frame.size
}
}
func viewControllerForSelectedSegmentIndex(index: Int) -> UIViewController {
//instantiate and return your view controller here
}
Some other notes:
I disabled "resize view from NIB" in storyboard for all my view controllers i plan to add to the container

Dynamic UILabel size for view before uitableview

I am having abit of trouble here trying to make this post description label to grow and shrink based on content size on IOS9. I have a view (I will refer it topView) that I am using as a header for the tableview (So when I scroll up the header disappears). Inside the topView, there are a bunch of stack views. I wish to grow and shrink the post description label in height based on content size. I do know how to do it in simple case where everything is inside the prototype cell (i.e. set estimated row height and set uitableviewautomaticDimensions, set sizetoFit on label and change number of lines to 0). However, this is a different case because the post description label is not really inside the cell, it is in its view before the table view cells.
Note that all items in the view has static height except the postdescription label. Post description label is inside a stack view that is pinned only left and right (So that top and bottom would grow?). Also, the main stack view that contains all elements is pinned towards the four sides with the topview that contains the main stack view also pinned towards the four sides. With this setup, I would expect the topview to grow and shrink based on the content size. However, I do not see that in the output. I dont know if it is the stackview that is holding the label refusing to grow or the top view refusing to grow to allow more space for the stackview for the label. Thanks
UPDATE
Thanks Riadluke, I tried doing something as suggested which is resizing the headerview after calculating the required height. I have placed the following code in viewDidLayoutSubview and it works with an issue
postDescriptionLbl.sizeToFit()
let headerView = commentTableView.tableHeaderView!
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = TopStackView.frame.size.height + ImageStackView.frame.size.height + postDescriptionLbl.frame.size.height + SpacerStackView.frame.size.height + BottomStackView.frame.size.height
headerView.frame.size.height = height
commentTableView.tableHeaderView = headerView
The issue I now have with this method is that when the view controller appears, I can physically see the postDescription label height grow from the default height in storyboard to the required height. For example, when the VC first appears, I see a line of label with some string being cut off, however after 0.5 second, the headerview and the label grow to the size that I wanted. I know this would be expected because I was calling the manipulation after the viewDidLayout Subview. I was wondering if there is a better way such that I dont see that transition and the view appears to be the right height straight away. Ie. let the view know exactly how high the label is to determine how high the headerview needs to be before appearing on screen?
I'm afraid the view set as tableHeaderView of a UITableView does not get resized automatically. Its height will be fixed to the height it had in IB.
What you have to do is set its size manually and then reassign it as the tableHeaderView so it is displayed in the height you want.
It could take only few lines since you're using autolayout.
You can try this code right after you've set the header view's contents:
//for the target size you have set the width as your tableView's width when it is already displayed on screen.
//note that when it is accesed inside viewDidLoad the tableView's bounds
//may be different to the actual bounds it will be displayed with,
//here I am just using the screen bounds
let targetSize = CGSize(width: UIScreen.mainScreen().bounds.width, height: 10000)
//set the tableHeader's size to its size after its layout constraints are resolved
tableHeader.bounds.size = tableHeader.systemLayoutSizeFittingSize(targetSize)
//reassign it as the tableHeaderView to update the height it will be displayed in
tableView.tableHeaderView = tableHeader
After many many attempts, I could not get anything to work with the original setup. The best I achieved was to resize it after view did appear which is not idea as you see the previous layout.
It is now working with a complete different approach. I have created two prototype cell and have one as "HeaderViewCell" and implemented the following functions
commentTableView.estimatedSectionHeaderHeight = 400
commentTableView.sectionHeaderHeight = UITableViewAutomaticDimension
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
Everything works like a charm after that.

Add a ScrollView to existing View

I'm developing a little app in Swift 2.0. I have a View with the following hierarchy:
Now, the elements placed in this view can't be displayed entirely in it, so I would like to use a ScrollView in order to be able to scroll all the content.
How I can embed all the content of my Viewto a new ScrollView? Can I do this programmatically by code?
UPDATE: There's an easier way to do this which I didn't know when I posted this answer
1) Go to the viewcontroller on the storyboard and click on something in it and press Command + A. This will select all the elements in the view.
2) Go to Editor -> Embed In -> Scroll View. This will embed all the labels, views, etc into a scrollView.
3) Select the scrollview that you just embedded. Press Control and drag it to the respective class file of the viewcontroller and create an outlet scrollView.
4) Add this to your viewDidLoad()
scrollView.contentSize = CGSizeMake(self.view.frame.width, self.view.frame.height+100)
Make sure the height for the contentSize is more than the size of your view.
ADDING SCROLLVIEW MANUALLY
1) Drag and drop a scrollview from the Object Library onto your viewcontroller in the storyboard and create an outlet to your program as 'scrollView'.
2) Click on your viewcontroller and go to the size inspector and note down the width and height.
3) Click on your scrollview and set the width and height the same as the viewcontroller. and set the X and Y values to 0
4) Click on the scrollView and drag it a little bit to the side
5) Press Command+A to select all the elements including scrollView. Press Command and click on the scrollView to deselect the ScrollView
6)You will have all the elements except the scrollView selected now. Now click and drag them into the scrollView.
7) Now click on the scrollView and set the X and Y values to 0 from the Size Inspector.
8) Add this to your viewDidLoad()
scrollView.contentSize = CGSizeMake(self.view.frame.width, self.view.frame.height+100)
Make sure the height for the contentSize is more than the size of your view.
That should create a scrollView for your view. Some of the elements might be in a slightly different position. You can easily fix them by moving them on your storyBoard.
It can be done even simpeler than ebby94's answer.
1) Go to the viewcontroller on the storyboard and click on something in it and press Command + A. This will select all the elements in the view.
2) Go to Editor -> Embed In -> Scroll View. This will embed all the labels, views, etc into a scrollView.
3) Set the constraints of the Scroll View to the View's edges.
And you're good to go! No need for an outlet.
If you are using Snapkit or creating programmatically.
class ScrollViewController: UIViewController {
lazy var contentViewSize = CGSize(width: self.view.frame.width, height: self.view.frame.height + 320) //Step One
lazy var scrollView : UIScrollView = {
let view = UIScrollView(frame : .zero)
view.frame = self.view.bounds
view.contentInsetAdjustmentBehavior = .never
view.contentSize = contentViewSize
view.backgroundColor = .white
return view
}()
lazy var containerView : UIView = {
let view = UIView()
view.frame.size = contentViewSize
view.backgroundColor = .white
return view
}()
override func viewDidLoad() {
self.view.addSubview(scrollView)
self.scrollView.addSubview(containerView)
//Now Set Add your Constraints in the container View.
}
}
Above accepted answer explanation is enough to achieve the scroll view but I would prefer to create my complete application programatically and I don't use storyboards in my project. This code is for the folks who don't prefer to use storyboards.
Explanation
Step One: Determine your content Size. Here I am taking Exact width and adding 320 more to the height of the screen.
Step Two: Create a scroll view and add desire behaviour of the scroll view. Now, the contentSize of the scroll view should be same as the contentSize you've created above at step one.
By Following Step one and Step Two. You Will be able to set a scroll view on the top of the view. But If you want to add a stretching behaviour then You should follow Step Three
Step Three: Create a container view of the same size of the contentView which you've calculated in step one and set it to the frame of the containerView. By doing this you'll be able to achieve stretching header and footer behaviour in your screen.
Please read make sure to add constraints in the same order as it is set.
Answer Edits are welcome.
I had the same issue. I needed to add scrollview to the existing view.But my main container view has a lots of view inside it. And they were connected to each other. So i was afraid. Finally i did it. the process given below.
Duplicate your View (root View). For this first select the root view then press Command + D
Now delete all the child view view inside the root view
Now add a scroll view to the root view and set constraint to 0,0,0,0
Now add the duplicate(That you duplicated) view to the scroll view and set constraints to 0,0,0,,0 also set the height that you want.
Set width of the duplicate view by equal width with the root view.
Now select the viewController, go the size inspector, select free form size. Then set the height that you entered with duplicate view.
You have almost done. Now you have to connect the child view with outlet or action that you gave in the viewController class.
Thats all.
Selecting all elements and embedding scroll view (editor->embed in->scrollView) works fine.
Adding constraint is much more easy by selecting the constraint warning (Add Missing Constraints).
It's simple. Command A and Command X to specific view controller in StoryBoard. After that take scroll view. On scroll view, just take one view with view controller view height and width equal to scroll View width. Again do Command V and rearrange the constraints. Your problem will be solved.

Can't change default layoutMargins of view

Since ios 8.0 views have additional layoutMargins which by default has an 8 points values for every side.
When I try to change margins in viewDidLoad it seems to have no effect on the child views:
override func viewDidLoad(){
super.viewDidLoad()
self.view.layoutMargins = UIEdgeInsets(top:100, left:100, bottom:100, right:100)
}
..itd does not seem to have any effect and
You are trying to set the layout margin of a root view of a view controller. The root view behaves differently than any other view in the hierarchy in regard of the layout margins.
Layout margins of a root view are managed exclusively by the view controller, you can not set them.
From Apple documentation:
If the view is a view controller’s root view, the system sets and manages the margins. The top and bottom margins are set to zero points. The side margins vary depending on the current size class, but can be either 16 or 20 points. You cannot change these margins.
https://developer.apple.com/reference/uikit/uiview/1622566-layoutmargins
I used KVO to detect the view.layoutMargins changes. And I found that the view itself would change the layoutMargins after customise during layoutSubViews processing.
So just put your customise codes in viewDidLayoutSubviews method:
- (void)viewDidLayoutSubviews
{
self.view.layoutMargins = UIEdgeInsetsMake(100.0f, 100.0f, 100.0f, 100.0f);
}
PS: Through these codes are OC, but I think it can also work for Swift. Hope this works for you!
I am not sure what the problem is.
override func viewDidLoad() {
super.viewDidLoad()
self.view.layoutMargins = UIEdgeInsets(top:100, left:100, bottom:100, right:100)
println(self.view.layoutMargins.top)
println(self.view.layoutMargins.left)
println(self.view.layoutMargins.right)
println(self.view.layoutMargins.bottom)
}
This prints 100 for every margin. If you are trying to set the margins for subviews of that view, you will need to set them explicitly also. The call to self.view.layoutMargins only affects the layoutMargins of self.view.
If you want your view's subviews to respect the layoutMargins of your view's superview, you will need to set preservesSuperviewLayoutMargins on your view.
Reference Documentation

UIScrollView not scrolling

I have a UIScrollView which contains many UIImageViews, UILabels, etc... the labels are much longer that the UIScrollView, but when I run the app, I cannot click and scroll down...
Why might this be?
Thanks
It's always good to show a complete working code snippet:
// in viewDidLoad (if using Autolayout check note below):
UIScrollView *myScrollView;
UIView *contentView;
// scrollview won't scroll unless content size explicitly set
[myScrollView addSubview:contentView];//if the contentView is not already inside your scrollview in your xib/StoryBoard doc
myScrollView.contentSize = contentView.frame.size; //sets ScrollView content size
Swift 4.0
let myScrollView
let contentView
// scrollview won't scroll unless content size explicitly set
myScrollView.addSubview(contentView)//if the contentView is not already inside your scrollview in your xib/StoryBoard doc
myScrollView.contentSize = contentView.frame.size //sets ScrollView content size
I have not found a way to set contentSize in IB (as of Xcode 5.0).
Note:
If you are using Autolayout the best place to put this code is inside the -(void)viewDidLayoutSubviews method .
If you cannot scroll the view even after you set contentSize correctly,
make sure you uncheck "Use AutoLayout" in Interface Builder -> File Inspector.
You need to set the contentSize property of the scroll view in order for it to scroll properly.
If you're using autolayout, you need to set contentSize in viewDidLayoutSubviews in order for it to be applied after the autolayout completes.
The code could look like this:
-(void)viewDidLayoutSubviews
{
// The scrollview needs to know the content size for it to work correctly
self.scrollView.contentSize = CGSizeMake(
self.scrollContent.frame.size.width,
self.scrollContent.frame.size.height + 300
);
}
The answer above is correct - to make scrolling happen, it's necessary to set the content size.
If you're using interface builder a neat way to do this is with user defined runtime attributes. Eg:
Try to resize the content size to huge numbers. I couldn't understand why my scroll view doesn't scroll even when its content size seems to be bigger than control size. I discovered that if the content size is smaller than needed, it doesn't work also.
self.scrollView.contentSize = CGSizeMake(2000, 2000);
Instead of 2000 you can put your own big numbers. And if it works, it means that your content size is not big enough when you resize.
The delegate is not necessary for scroll view to work.
Make sure you have the contentSize property of the scroll view set to the correct size (ie, one large enough to encompass all your content.)
Uncheck 'Use Autolayout' did the trick for me.
Environment:
xCode 5.0.2
Storyboards
ios7
In my case I had to set delaysContentTouches to true because the objects inside the scrollView were all capturing the touch events and handling themselves rather than letting the scrollView itself handle it.
Set contentSize property of UIScrollview in ViewDidLayoutSubviews method. Something like this
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = CGSizeMake(view.frame.size.width, view.frame.size.height)
}
if you are getting a message (IOS8 / swift) that viewDidLayoutSubviews does not exist, use the following instead
override func viewDidAppear(animated: Bool)
This fixed it for me
The idea of why scroll view is not scrolling because you set the content size for scrolling less than the size of the scroll view, which is wrong.
You should set the content size bigger than the size of your scroll view to navigate through it while scrolling.
The same idea with zooming, you set the min and max value for zooming which will applied through zooming action.
welcome :)
One small addition, all above are the actual reasons why your scroll view might not be scrolling but sometimes mindlessly this could be the reason specially when scrollview is added through code and not IB, you might have added your subviews to the parent view and not to the scrollview this causes the subview to not scroll
and do keep the content size set and bigger than parent view frame (duhh!!)
I made it working at my first try. With auto layout and everything, no additional code. Then a collection view went banana, crashing at run time, I couldn't find what was wrong, so I deleted and recreated it (I am using Xcode 10 Beta 4. It felt like a bug) and then the scrolling was gone. The Collection view worked again, though!
Many hours later.. this is what fixed it for me. I had the following layout:
UIView
Safe Area
Scroll view
Content view
It's all in the constraints. Safe Area is automatically defined by the system. In the worst case remove all constraints for scroll and content views and do not have IB resetting/creating them for you. Make them manually, it works.
For Scroll view I did: Align Trailing/Top to Safe Area. Equal Width/Height to Safe area.
For Content view I did: Align Trailing/Leading/Top/Bottom to Superview (the scroll view)
basically the concept is to have Content view fitting Scrollview, which is fitting Safe Area.
But as such it didn't work. Content view missed the height. I tried all I could and the only one doing the trick has been a Content view height created control-dragging Content view.. to itself. That defined a fixed height, which value has been computed from the Size of the the view controller (defined as freeform, longer than the real display, to containing all my subviews) and finally it worked again!
Add the UIScrollViewDelegate and adding the following code to the viewDidAppear method fixed it for me.
#interface testScrollViewController () <UIScrollViewDelegate>
-(void)viewDidAppear:(BOOL)animated {
self.scrollView.delegate = self;
self.scrollView.scrollEnabled = YES;
self.scrollView.contentSize = CGSizeMake(375, 800);
}
My issue was resolved by:
setting the contentSize on the scrollView to a large height
BUT also I had to fix top and/or bottom constraints on views within the scrollView, which meant the scroll indicators showed on screen but the content did not scroll
Once I removed top and/or bottom constraints bound to the safe area and/or superview, the views inside the scrollView could scroll again and didn't stay fixed to the top of bottom of the screen!
Hope this stops someone else from hours of pain with this particular issue.
yet another fun case:
scrollview.superview.userInteractionEnabled must be true
I wasted 2+hrs chasing this just to figure out the parent
is UIImageView which, naturally, has userInteractionEnabled == false
Something that wasn't mentioned before!
Make sure your outlet was correctly connected to the scrollView! It should have a filled circle, but even if you have filled circle, scrollView may not been connected - so double check! Hover over the circle and see if the actual scrollview gets highlighted! (This was a case for me)
//Connect below well to the scrollView in the storyBoard
#property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
Alot of the time the code is correct if you have followed a tutorial but what many beginners do not know is that the scrollView is NOT going to scroll normally through the simulator. It is suppose to scroll only when you press down on the mousepad and simultaneously scroll. Many Experienced XCode/Swift/Obj-C users are so use to doing this and so they do not know how it could possibly be overlooked by beginners. Ciao :-)
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(scrollView)
// Do any additional setup after the view
}
override func viewWillLayoutSubviews(){
super.viewWillLayoutSubviews()
scrollView.contentSize = CGSize(width: 375, height: 800)
}
This code will work perfectly fine as long as you do what I said up above
If none of the other solutions work for you, double check that your scroll view actually is a UIScrollView in Interface Builder.
At some point in the last few days, my UIScrollView spontaneously changed type to a UIView, even though its class said UIScrollView in the inspector. I'm using Xcode 5.1 (5B130a).
You can either create a new scroll view and copy the measurements, settings and constraints from the old view, or you can manually change your view to a UIScrollView in the xib file. I did a compare and found the following differences:
Original:
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" directionalLockEnabled="YES" bounces="NO" pagingEnabled="YES" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wsk-WB-LMH">
...
</scrollView>
After type spontaneously changed:
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" customClass="UIScrollView" id="qRn-TP-cXd">
...
</view>
So I replaced the <view> line with my original <scrollView> line.
I also replaced the view's close tag </view> with </scrollView>.
Be sure to keep the id the same as the current view, in this case: id="qRn-TP-cXd".
I also had to flush the xib from Xcode's cache by deleting the app's derived data:
Xcode->Window->Organizer->Projects, choose your project, on the Derived Data line, click Delete...
Or if using a device:
Xcode->Window->Organizer->Device, choose your device->Applications, choose your app, click (-)
Now clean the project, and remove the app from the simulator/device:
Xcode->Product->Clean
iOS Simulator/device->press and hold the app->click the (X) to remove it
You should then be able to build and run your app and have scrolling functionality again.
P.S. I didn't have to set the scroll view's content size in viewDidLayoutSubviews or turn off auto layout, but YMMV.
If your scrollView is a subview of a containerView of some type, then make sure that your scrollView is within the frame or bounds of the containerView. I had containerView.clipsToBounds = NO which still allowed me see the scrollView, but because scrollView wasn't within the bounds of containerView it wouldn't detect touch events.
For example:
containerView.frame = CGRectMake(0, 0, 200, 200);
scrollView.frame = CGRectMake(0, 200, 200, 200);
[containerView addSubview:scrollView];
scrollView.userInteractionEnabled = YES;
You will be able to see the scrollView but it won't receive user interactions.
adding the following code in viewDidLayoutSubviews worked for me with Autolayout. After trying all the answers:
- (void)viewDidLayoutSubviews
{
self.activationScrollView.contentSize = CGSizeMake(IPHONE_SCREEN_WIDTH, 620);
}
//set the height of content size as required
The straightforward programmatically way
To wrap it up
Create a UIScrollView
private lazy var scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
Use a Single Child View to Hold All of Your Content Subviews
private lazy var contentView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
Add your views
contentView.addSubview(firstSubView)
contentView.addSubview(lastSubView)
scrollView.addSubview(contentView)
view.addSubview(scrollView)
Usually, you only want your content to scroll in one direction. In most cases to scroll vertically. Therefore set the width of the content view to be the width of the scroll view.
NSLayoutConstraint.activate([
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
Attach four constraints (top, bottom, left, right) from our single content view to the scroll view.
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
Make sure you have constraints attached to all four sides of the content view so that it will expand to the size of your content.
// After Adding your subviews to the contentView make sure you've those two constraints set:
firstSubView.topAnchor.constraint(equalTo: contentView.topAnchor),
.
.
.
lastSubView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
])
Reference: Using UIScrollView with Auto Layout in iOS
After failing with the provided answers in this thread, I stumbled upon this article with the solution.
There are two things not intuitive about setting up the scrollview with autolayout:
The constraints you set up as margin between the contentview and scrollview do not influence the size of the contentview. They really are margins. And to make it work, the contentview should have a fixed size.
The trick to the fixed size is that you can set the width of the contentview equal to that of the scrollview's parent. Just select both views in the tree on the left and add the equal widths constraint.
This is the gist of that article. For a complete explanation, including illustrations, check it out.
I found that with this AutoLayout issue... if I just make the ViewController use UIView instead of UIScrollView for the class... then just add a UIScrollView myself... that it works.
I had the same issue in IB.
After setting the leading, trailing, top and bottom of the scrollView to its superView. I made the following changes to make the containerView scrollable, which worked.
To make the scrollView only scroll on horizontal direction make the constraint with scrollView's centerY = ContainerView's centerY
and to make it vertically scrollable make the scrollView's centerX = ContainerView's centerX
You don’t have to set the content size of the scroll view.
Technical Note TN2154
In case someone made the same mistake like me, I'd like to share my case.
In my case, I mistakenly add a constraint to one of the subviews of scrollview which makes the subview's space to the topLayoutGuide fixed, thus it's location can't be changed, so the scrollview can't be scrolled.

Resources