01Riv
01Riv

Reputation: 1454

Using Scroll View with Autolayout Swift

I know this question has been asked many times but this is a problem I have been struggling with for a long time and I'm sure others are too, even with the current answers and tutorials out there.

When adding a Scroll View I go through the following steps:

  1. Add a Scroll View as a subview of the original view in the view controller. Pin top, left, right, and bottom. Ensuring that "Constrain to margins" is unchecked.

  2. Add a UIView as a subview of the Scroll View. Pin top, left, right, and bottom constraints.

  3. Add an equal widths constraint between the content view and the view controllers view.

At this point of I run the app the content view does not appear and the scroll View takes up the entire screen.

  1. Next I add elements to the Content View that just include 4 UIViews to test everything. I give each UIView top, left, and right constraints. And the last UIView a bottom constraint.

Not at this point when I run the app the Content View and Scroll View each take up about half of the screen and I can scroll the Content View around. See below photo.

I have followed every tutorial I can find and tried implementing all SO answers I have found but I can't seem to get it to work. If anybody has come across this or knows a solution your help would be very much appreciated!

The green is the Content View and the blue is the Scroll View

enter image description here

Scroll View and Subview constraints

enter image description here

Upvotes: 23

Views: 46525

Answers (14)

nathanwhy
nathanwhy

Reputation: 6144

All the above answers are great, let me add a bit more. Do not add safe area constraints to subviews in the scrolling direction.

For example, the view can scroll vertically, Do not add top/bottom safe area constraints to the subviews of the scroll view.

Upvotes: 0

user2201
user2201

Reputation: 39

Simple way to add ScrollView:

  1. add Scrollview in view contrller
  2. add left,right,top and bottom constraint of scrollview to its superview
  3. add UIView inside scrollview
  4. add left,right,top and bottom constraint of UIView to content layout guide
  5. add equal width constraint of UIview to Frame layout Guide
  6. add your sub component now (if you just want to check scrollview is working or not just give fix height and check)

Upvotes: 1

Mark
Mark

Reputation: 11359

How to do it programmatically?

Remember : (your main view) -> (your scroll view) -> (your content view) -> all your stuff

As of Swift4

    let scrollView = UIScrollView()
    scrollView.translatesAutoresizingMaskIntoConstraints = false
    addSubview(scrollView)
    
    NSLayoutConstraint.activate([scrollView.topAnchor.constraint(equalTo: topAnchor),
                                 scrollView.leadingAnchor.constraint(equalTo: leadingAnchor),
                                 scrollView.trailingAnchor.constraint(equalTo: trailingAnchor),
                                 scrollView.bottomAnchor.constraint(equalTo: bottomAnchor)])
    
    let contentView = UIView()
    contentView.translatesAutoresizingMaskIntoConstraints = false
    scrollView.addSubview(contentView)
    
    NSLayoutConstraint.activate([contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
                                 contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
                                 contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
                                 contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
                                 contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor)])

Then you can add other subviews in ContentView

Troubleshooting:

Make sure every constraint is connecting from contentView.TopAnchor to contentView.BottomAnchor so you will have your contentSize dynamically

Upvotes: 1

korgx9
korgx9

Reputation: 1264

Xcode 11 Swift 5

More info here

In order for scrollview to work in Auto Layout, scrollview must know its scrollable content (scrollview content) width and height , and also the frame (X, Y , Width, Height) of itself, ie. where should the superview place scrollview and what size.

With Xcode 11 two new things Content Layout guide and Frame Layout guide were introduced in Interface Builder.

Now for making work scrollView from storyboard you should:

  • Put a scroll view into the view controller and set constraints (leading, top, trailing, bottom - all 0)
  • Put a view(contentView) inside the scroll view and set constraints (leading, top, trailing, bottom - all 0)
  • In views hierarchy, do ctrl drag from new View(contentView) to Content Layout guide of scrollView and in the appeared context menu choose all constraints (leading, top, trailing, bottom - all 0)
  • If we want to make the scrollview scroll only vertically, create an equal width constraint between the view and the scrollview's Frame Layout guide (for horizontally create equal height constraint)
  • Place all your content view elements inside it and create all vertical constraints in the way that the top and the bottom of the contentView must be connected by constraints inside it.

That's it

Upvotes: 26

ShaileshAher
ShaileshAher

Reputation: 823

Use this ScrollView, and use contentView similar to that of UITableViewCell's/UICollectionViewCell's content view

class ScrollView: UIScrollView {

lazy var contentView : UIView = {
    let contentView = UIView(frame: .zero)
    contentView.translatesAutoresizingMaskIntoConstraints = false
    contentView.backgroundColor = .orange
    return contentView
}()

override init(frame: CGRect) {
    super.init(frame: frame)
    viewSetup()
}

required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
}

private func viewSetup() {
    addSubview(contentView)
    setupConstraints()
}

private func setupConstraints() {

    NSLayoutConstraint.activate([
        contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
        contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
        contentView.topAnchor.constraint(equalTo: topAnchor),
        contentView.bottomAnchor.constraint(equalTo: bottomAnchor),
        contentView.widthAnchor.constraint(equalTo: widthAnchor)
    ])

    let contraint = contentView.heightAnchor.constraint(equalTo: heightAnchor)
    contraint.priority = .defaultLow
    contraint.isActive = true
}

}

Upvotes: 1

Gurjinder Singh
Gurjinder Singh

Reputation: 10329

Just want to show visually. If you want to create a view which is larger than the size of ViewController then you can increase the height of ViewController form size inspector. Choose Simulated Size to Freeform and set required height as shown in image and now your viewController has the mentioned height.

enter image description here

  1. Drag Scrollview into your ViewController and apply four constraints (Leading, Trailing, Top and Bottom) enter image description here

  2. Now drag one view having same width and height as of ScrollView inside ScrollView and set 6 constraints (Leading, Trailing, Top, Bottom, FixHeight, =WidthToScrollView). In my case I have 15 L and T space and other margins accordingly. You view may have different margin but constraints should be same.

enter image description here. enter image description here

  1. Now you can drag your views accordingly and set constraints. I am having two view inside the view.

enter image description here

  1. For 1st view I set Leading, Top, Trailing and want to have fix height. For 2nd I set Leading, bottom, trailing and want to have fix height.

enter image description here. enter image description here

  1. Congratulation!. You have all set now run your app.

enter image description hereenter image description here

Upvotes: 2

Sahdevsinh Chavda
Sahdevsinh Chavda

Reputation: 526

Follow this steps:

  1. Add a Scroll View as a Sub View of the Main View.
  2. Select the Scroll View and uncheck "Constrain to margins" and pin top, left, right, bottom, constraints
  3. Add a UIView as a subview of the Scroll View. Name this view "Content View"
  4. Set “Content View” constraint (top, bottom, leading and trailing) as (0,0,0,0).
  5. Now you can see hierarchy like this view -> scroll view -> view(content view)
  6. Our “Content view” must have equal width and equal height with parent view.
  7. Select height constraint of content view and set priority with low(250).
  8. Now you can design your view with any height.
  9. Add whatever elements you need inside the Content View. Pin top, left, right, and height constraints to the elements that were just added.
  10. Congrats you are done with scroll view with autolayout.


Upvotes: 3

nmh
nmh

Reputation: 2503

To those who are looking for an example UIScrollView and AutoLayout. I am using SnapKit, it is working for Xcode 9, Swift 3

    let scrollView = UIScrollView()
    view.addSubview(scrollView)
    scrollView.snp.makeConstraints { (make) in
        make.edges.equalTo(view).inset(UIEdgeInsetsMake(0, 0, 0, 0))
        make.top.left.right.equalTo(view)
    }

    let view1 = UIView()
    view1.backgroundColor = UIColor.red
    scrollView.addSubview(view1)
    view1.snp.makeConstraints { (make) in
        make.top.equalTo(scrollView.snp.top).offset(0)
        make.left.equalTo(scrollView.snp.left).offset(0)
        make.width.equalTo(scrollView.snp.width)
        make.height.equalTo(300)
    }

    let view2 = UIView()
    view2.backgroundColor = UIColor.blue
    scrollView.addSubview(view2)
    view2.snp.makeConstraints { (make) in
        make.top.equalTo(view1.snp.bottom)
        make.left.equalTo(scrollView)
        make.width.equalTo(scrollView)
        make.height.equalTo(300)
    }

    let view3 = UIView()
    view3.backgroundColor = UIColor.green
    scrollView.addSubview(view3)
    view3.snp.makeConstraints { (make) in
        make.top.equalTo(view2.snp.bottom)
        make.left.equalTo(scrollView)
        make.width.equalTo(scrollView)
        make.height.equalTo(100)
        make.bottom.equalTo(scrollView.snp.bottom).offset(-20)
    }

Upvotes: 2

Fahad Azeem
Fahad Azeem

Reputation: 651

I have spend alot of time to figure this out and its pretty simple. Here is the solution. Follow these steps

  • Add ScrollView as subview of the main view
  • Pin top, left, right, bottom, horizontally in the container, Vertically in the container.
  • Add container view as subview of scroll view
  • Pin top, left, right, bottom, horizontally in the container, Vertically in the container.
  • Add your subviews in container view with desired constraints.
  • Set the content size of the scroll view accordingly.

Keep scrolling :)

Upvotes: 3

Bowcaps
Bowcaps

Reputation: 127

As per @m1234, however you need to release the equal heights otherwise the scroll does not work - my method would be as thus :

  1. Add a Scroll View as a Sub View of the Main View.

  2. Select the Scroll View and uncheck "Constrain to margins" and pin top, left, right, bottom, constraints

  3. Add a UIView as a subview of the Scroll View. Name this view "Content View"

  4. Select the Content View and pin top, left, right, and bottom constraints. Then add a center horizontally constraint.

  5. Next from the Content View to the Main View add an equal width constraint.

  6. Add whatever elements you need inside the Content View. Pin top, left, right, and height constraints to the elements that were just added.

  7. On the bottom most item inside the Content View pin a bottom constraint. Select this constraint and change to "Greater Than or Equal". Change the constant to 20.

Upvotes: 0

01Riv
01Riv

Reputation: 1454

I figured this out with the help of the other answers but I had to make some adjustments to get it work the way I wanted. Here are the steps I took:

  1. Add a Scroll View as a Sub View of the Main View.

  2. Select the Scroll View and uncheck "Constrain to margins" and pin top, left, right, bottom, constraints

  3. Add a UIView as a subview of the Scroll View. Name this view "Content View"

  4. Select the Content View and pin top, left, right, and bottom constraints. Then add a center horizontally constraint.

  5. Next from the Content View to the Main View add equal width and equal height constraints.

  6. Add whatever elements you need inside the Content View. Pin top, left, right, and height constraints to the elements that were just added.

  7. On the bottom most item inside the Content View pin a bottom constraint. Select this constraint and change to "Greater Than or Equal". Change the constant to 20.

The constraints added to the items inside the Content View are very important, especially the bottom constraint added to the last item. They help to determine the content size of the scroll view. Adding the bottom constrain as I described will enable the view to scroll if the content is too large to fit in the screen, and disable scrolling if the content does fit in the screen.

Upvotes: 78

Randel S
Randel S

Reputation: 362

The best way I know how to create an UI inside a scrollView without code is:

  1. Add your scrollView to the uiviewcontroller with proper constraints between it and its parent view.
  2. Add a “brace view” to the scrollview.
  3. “Brace view” should be hidden since it’s not apart of your final UI.
  4. Make left, top, right contains between “brace view” and the UIScrollView. Make height constraint. (Height constraint can be any number). Make align center x to UIScrollView constraints.

  5. Add your final UI beneath the “brace view”.

  6. Add top constraint between the first final UI view and the “brace view”.
  7. Link all your final UIs view together with top or bottom constraints.
  8. Add proper final UI constraints.
  9. Add bottom constraints between the last final UI view and the scroll view. ( This constraint.constant is important because it’s apart of your final UI).

Here's a quick github project example.

enter image description here enter image description here

Upvotes: 0

Tomasz Nazarenko
Tomasz Nazarenko

Reputation: 1204

I have made a simple view in code that should be self explanatory and might help you. It outlines all the steps you need to take to make the scroll view working.

If something is not clear, feel free to drop a comment.

import UIKit

class TutorialView: UIView {

    lazy var sv: UIScrollView = {
        let object = UIScrollView()

        object.backgroundColor = UIColor.whiteColor()
        object.translatesAutoresizingMaskIntoConstraints = false
        return object
    }()

    lazy var tutorialPageOne: UIView = {
        let object = UIView(frame: UIScreen.mainScreen().bounds)
        object.translatesAutoresizingMaskIntoConstraints = false
        object.backgroundColor = UIColor.cyanColor()

        return object
    }()

    lazy var tutorialPageTwo: UIView = {
        let object = UIView(frame: UIScreen.mainScreen().bounds)
        object.translatesAutoresizingMaskIntoConstraints = false
        object.backgroundColor = UIColor.lightGrayColor()

        return object
    }()

    lazy var tutorialPageThree: UIView = {
        let object = UIView(frame: UIScreen.mainScreen().bounds)
        object.translatesAutoresizingMaskIntoConstraints = false
        object.backgroundColor = UIColor.redColor()

        return object
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)

        self.addSubview(self.sv)
        self.sv.addSubview(self.tutorialPageOne)
        self.sv.addSubview(self.tutorialPageTwo)
        self.sv.addSubview(self.tutorialPageThree)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        let vc = nextResponder() as? UIViewController
        let mainSreenWidth = UIScreen.mainScreen().bounds.size.width
        let mainScreenHeight = UIScreen.mainScreen().bounds.size.height

        NSLayoutConstraint.activateConstraints([
            self.sv.topAnchor.constraintEqualToAnchor(vc?.topLayoutGuide.bottomAnchor),
            self.sv.leadingAnchor.constraintEqualToAnchor(self.leadingAnchor),
            self.sv.bottomAnchor.constraintEqualToAnchor(vc?.bottomLayoutGuide.topAnchor),
            self.sv.trailingAnchor.constraintEqualToAnchor(self.trailingAnchor)
        ])

        NSLayoutConstraint.activateConstraints([
            self.tutorialPageOne.widthAnchor.constraintEqualToConstant(mainSreenWidth),
            self.tutorialPageOne.heightAnchor.constraintEqualToConstant(mainScreenHeight),
            self.tutorialPageOne.topAnchor.constraintEqualToAnchor(self.sv.topAnchor),
            self.tutorialPageOne.leadingAnchor.constraintEqualToAnchor(self.sv.leadingAnchor),
            self.tutorialPageOne.bottomAnchor.constraintEqualToAnchor(self.sv.bottomAnchor)
        ])


        NSLayoutConstraint.activateConstraints([
            self.tutorialPageTwo.widthAnchor.constraintEqualToConstant(mainSreenWidth),
            self.tutorialPageTwo.heightAnchor.constraintEqualToConstant(mainScreenHeight),
            self.tutorialPageTwo.topAnchor.constraintEqualToAnchor(self.sv.topAnchor),
            self.tutorialPageTwo.leadingAnchor.constraintEqualToAnchor(self.tutorialPageOne.trailingAnchor),
            self.tutorialPageTwo.bottomAnchor.constraintEqualToAnchor(self.sv.bottomAnchor)
        ])

        NSLayoutConstraint.activateConstraints([
            self.tutorialPageThree.widthAnchor.constraintEqualToConstant(mainSreenWidth),
            self.tutorialPageThree.heightAnchor.constraintEqualToConstant(mainScreenHeight),
            self.tutorialPageThree.topAnchor.constraintEqualToAnchor(self.sv.topAnchor),
            self.tutorialPageThree.leadingAnchor.constraintEqualToAnchor(self.tutorialPageTwo.trailingAnchor),
            self.tutorialPageThree.bottomAnchor.constraintEqualToAnchor(self.sv.bottomAnchor),
            self.tutorialPageThree.trailingAnchor.constraintEqualToAnchor(self.sv.trailingAnchor)
        ])

    }
}

Upvotes: 5

Joride
Joride

Reputation: 3763

The UIScrollView class scrolls its content by changing the origin of its bounds. To make this work with Auto Layout, the top, left, bottom, and right edges within a scroll view now mean the edges of its content view.

The constraints on the subviews of the scroll view must result in a size to fill, which is then interpreted as the content size of the scroll view. (This should not be confused with the intrinsicContentSize method used for Auto Layout.) To size the scroll view’s frame with Auto Layout, constraints must either be explicit regarding the width and height of the scroll view, or the edges of the scroll view must be tied to views outside of its subtree.

See also a Technote Apple wrote about this issue: https://developer.apple.com/library/ios/technotes/tn2154/_index.html

Upvotes: 0

Related Questions