Morpheus
Morpheus

Reputation: 1289

View at the bottom in a UIScrollView, with AutoLayout

I'm setting up content in a scroll view with autolayout. The objects in the scrollview are pinned top-to-bottom to the previous one, so that they are under one another. I have a footer view that is added at the end, below these objects.

Here's the catch: when there's few content, the contentView will be smaller than the screen height, so the footer view will appear somewhere in the middle of the screen (which is the normal behavior). But I'd like to prevent that, and make the view stay somewhere at the bottom.

In other words, I would like to setup a double constraint like:

Put this view below all the objects in the scrollview 
AND
keep this view at a distance of max [some number] of the bottom of the screen

In a way that both constraints are always satisfied:

How can I achieve that with AutoLayout?

Upvotes: 23

Views: 14198

Answers (3)

HAK
HAK

Reputation: 2071

For scrollview with Content Layout Guides and Frame Layout Guides, the above solutions might not work. But the same can be achieved by following these steps:

  1. Add scrollview to your main view. Add leading, trailing, top and bottom constraints with the superview (ignore the content size ambiguity errors for height and width)

step-1

  1. Add container view to your scrollview.

  2. Add top, bottom, leading trailing constraints of container view to scrollview's ContentLayoutGuides.

step-3

  1. Add Equal Widths constraint b/w container view and FrameLayoutGuide. This will fix the content horizontally and will allow vertical scroll.

  2. Add Equal Height constraint b/w container view and FrameLayoutGuide and set the priority to 999. This will allow the container view to maintain a height equal to scrollview when content size is smaller but at the same time will allow to scroll when content size is greater than the scrollview's height.

step-5

Now lets say you have two views, a UILabel and a UIButton. You want your label to be fixed at the top and grow when it has more text. And you want your button to stay fixed at the bottom even if text is single line and maintain a distance (e.g 100 points) with the label when label grows. Follow the steps to achieve this:

  1. Add a UILabel in your container view, give it top, leading and trailing constraints and make no. of lines equal to zero.

step-2-1

  1. Add a UIButton in your container view, give it bottom, leading and trailing.

step2-2

  1. Add a vertical spacing constraint with Greater Than or Equal relation and a constant value of the min spacing required. At this point you are done but to satisfy Xcode, we need to add one more dummy constraint which we will remove at build time.

  2. Set constraint vertical spacing b/w label and button and set priority to 750. Check Remove at build time. This is a dummy constraint just to satisfy Xcode.

step2-3

Here is the output with single line text:

output-1

And this is how it will look with very long text:

output-2

Upvotes: 1

DonMag
DonMag

Reputation: 77423

Fairly easy to do with Auto-Layout only... no code required.

The key is to use a "content view" to hold the elements, and a greater-than-or-equal constraint between your "bottom" element and your "footer" view.

In this image, yellow is the main view, green is the scroll view, blue is the content view, the labels are gray and the footer view is pink.

enter image description here

  • Start with a fresh view controller
  • add a scroll view, normal constraints (I used 20 all the way around, so we can see the frame)
  • add a UIView to the scrollView - this will be our "content view"
  • constrain contentView Top/Bottom/Leading/Trailing all equal to 0 to the scrollView
  • constrain both the Width and Height of the contentView equal to the scrollView
  • add your elements - here I used 3 labels
  • constrain the labels as usual... I used:
    • LabelA - Top/Leading/Trailing all at 20, vertical spacing to LabelB of 60
    • LabelB - Leading/Trailing at 20, vertical spacing to LabelC of 60
    • LabelC - Leading/Trailing at 20
  • LabelC is also set to Number of Lines: 0 so it will expand with multiple lines of text
  • Add a UIView as a "footer view" (I stuck a label in it)
  • constrain the footerView Leading/Trailing/Bottom all at 20 (so we can see the frame)
  • either set a Height constraint on footerView, or use its content to constrain its height
  • add a Vertical Spacing constraint from LabelC to footerView, and set it to >= 40
  • last step, change the Height constraint of contentView to Priority: 250

Now, as you expand/contract the height of LabelC, the footerView will keep at least 40-pts of vertical space. When LabelC gets big enough to "push" footerView below the bottom, scrollView will become scrollable.

Results:

enter image description here enter image description here enter image description here

Upvotes: 84

iOS Geek
iOS Geek

Reputation: 4855

you need to check ContentSize of scrollView and modify FooterView Top Constraint with the required Value

My class code

import UIKit

class scrollViewDrag: UIViewController
{
    /// ScrollView Outlet
    @IBOutlet weak var mainScrollView: UIScrollView!

    /// Footer View top spacing constraint
    @IBOutlet weak var footerViewTopConstraint: NSLayoutConstraint!

    /// Used for ScrollView Height
    var screenHeight = CGFloat()

    /// Did Load
    override func viewDidLoad() {
        super.viewDidLoad()

    }

    /// Function used to check for height 
    func checkForHeight(){
        /// Get scrollView Height
        screenHeight = mainScrollView.frame.size.height

        /// Check contentSize Height ?
        if mainScrollView.contentSize.height >= screenHeight {
            /// When ScrollView is having height greater than your scrollView Height
            /// Footer will scroll along other Views
        }
        else{
            /// Issue Case
            let spacingValue = screenHeight-mainScrollView.contentSize.height
            footerViewTopConstraint.constant = spacingValue
        }
    }

    /// Call the height function in DidAppear
    override func viewDidAppear(_ animated: Bool) {
        checkForHeight()
    }
}

Storyboard

enter image description here

I had used Four View with Equal Heights And at last a footerView is attached as Fourth View

FooterView Top Constraint

enter image description here

Top constraint used as footerViewTopConstraint

Output

Case 1 - Size is greater than scrollView Height

enter image description here

Case 2 - Expected Output

enter image description here

Upvotes: 1

Related Questions