Breaking down the Twitter Profile Screen Part I: Stretchy Header (iOS Swift 4)

Eduardo Carrillo
7 min readApr 2, 2018

--

Hi. In the following series of tutorials I will show you how to implement each feature of Twitter’s profile screen. This will be done without 3rd party frameworks nor will we manipulate a UIView’s frame. We will use Autolayout exclusively. The first thing we will do is build out the stretchy header.

Stretchy header

Note: In this tutorial we will be using a UIScrollView instead of a UITableView but the general strategy that I will demonstrate today is easily transferable to a UITableView/UICollectionView/Anything that is a subclass of UIScrollView

Prerequisite knowledge:

Fundamental iOS concepts (UIViewController lifecycle, UIViews, etc.)

Using Autolayout with UIScrollViews

Strategy:

  1. We will first set up autolayout for our UIScrollView
  2. Then inside the content view of our scrollview we will set up autolayout constraints for the imageview. The Top and Height constraints of our image view are key for the stretchy effect.
  3. Our view controller will extend UIScrollViewDelegate and implement the scrollviewDidScroll() function
  4. Inside our scrollviewDidScroll() function we will keep track of the y position of the scroll
  5. If we are scrolling up(pulling down) past the initial position of the scrollview we need to do 2 things. First, update the top constraint of the imageview to be the value of the scrollview’s vertical position.Then expand the height by adding the difference between our content offset and intial vertical content offset (which is 0).
  6. Celebrate, almost. If we leave the project as is the height will expand but the width won’t scale with the height. In order to make the width expand along with the height make sure to change the content mode of the image view to aspect fill instead of scale fill.

If you don’t have experience using Autolayout with UIScrollViews here is a great article: https://www.natashatherobot.com/ios-autolayout-scrollview/

Build a quick project with the above link then come back to this one.

OK let’s get started.

You can download my starter project here: https://github.com/EddieCurio/StretchHeaderMedium

Also if you get stuck on any of the parts you can switch to a finished branch.

Branches

Master — Has nothing done.

part1 — Completing Step 1

part2 — Completing Step 2

part6 — Completing Step 6

It has each part separated in to different branches just in case you get lost.

Step 1a: Download the starter project in Xcode

Notice that that project is pretty much empty except for a photo of a wave.

Step 1b: Setup the autolayout for your scrollview

  1. ) First add your scrollview to your UIViewController scene in your storyboard.

2.) We want the scroll view to occupy the whole screen so we will pin the edges to the superview (You could just pin it to the Safe Area as well it just depends on your Use Case) We will see why we are pinning to the parent view instead of the Safe Area in a later tutorial.

3.) Define your content view and setup content view constraints for your scroll view.

So we create the content view and place it inside the UIScrollView. Then we create a EQUAL WIDTH and EQUAL HEIGHT constraint between the content view and the scrollview’s parentview NOT THE SCROLLVIEW

If you accidentally pin the height on the scrollview, the scrollview will get confused and not know how to calculate its content height.

Read more on the article I mentioned earlier for more info. But the basic idea is you need to make the height of the content view either be constant or dependent on something other than the scrollview itself.

Then we can just pin the content view’s edges to the scrollview’s edges

Step 2 Add UIImageView to the scrollview and add constraints

1.) Add Top, Left, Right, and Height (128 for today, but for your apps it could be whatever you want) constraints to the imageview and after placing in our content view in the storyboard

Step 3: Create outlets to imageview constraints

We will be manipulating the imageview’s top and height constraint based off the scrollview’s vertical content offset property so we need references to them in our code.

Step 4: Create outlets from our scrollview

We also will need a reference of our scrollview in order to make our viewcontroller a delegate to the UIScrollViewDelegate protocol. This will allow us to implement the scrollviewDidScroll() function

We also want to implement the UIScrollViewDelegate protocol and make our view controller class the delegate for our scroll view that we set up in the storyboard.

At this point you should go ahead and run the app.

Boring non stretchy header

Go ahead and look at your console and notice what happens to the content offset value printed as you scroll up and down.

You should notice that as we scroll the screen upwards the content offset’s vertical position value decreases and as we scroll downwards the content offset’s vertical position value increases.

Step 5 Implement stretchy header logic

The first thing we want to do is if we scroll up too high, then we want the imageview to stick to the top. So let’s do this first.

First let’s define what “too high” is. We say we have gone “too high” if we scroll above the scroll view’s default configuration. This only happens when we scroll above our scroll view’s content vertical offset of 0. So when we scroll above 0, we are “too high” and should readjust the imageview’s constraints. This logic is easily translatable to code.

Step 5 Make image view top stick to “new top” when we scroll up.

In your scrollViewDidScroll() function we need to detect when we have gone “too high”

So we add the following code to do so

func scrollViewDidScroll(_ scrollView: UIScrollView) {    let offset = scrollView.contentOffset.y    if offset < 0{ //Whenever we go too high run this code block 
//Make imageview sticky
}}

Now what to we do after we go too high?

We make the top stick. How do we make the top stick? Well we need to calculate what is considered to be the “new top” of the screen. It turns out it’s just the content offset’s y value itself. So now we should have ….

func scrollViewDidScroll(_ scrollView: UIScrollView) {let offset = scrollView.contentOffset.ylet defaultTop = CGFloat(0)// If we have not scrolled too high then stick to default y pos
var currentTop = defaultTop
if offset < 0{ //Whenever we go too high run this code block
//The new top (y position) of the imageview
currentTop = offset
}
imageViewTop.constant = currentTop}

After adding this code run your app and it should look something like this.

Is it even working?

It may look like it’s doing nothing but compare this to the last time we ran the code and when we scroll up we no longer see any white gap above the imageview. In other words, it works!

Step 6: Make image view “expand” when we scroll up too high.

We want the image to expand proportionally to the amount that scroll view has moved upward. Say we move 10 units above our baseline of 0. Then we want to add 10 units to our imageview height.

Then, how do we calculate the numebr of units above 0 we have moved?

Just use the content offset again. If we go up 30 units past 0 then the vertical content offset value will have -30. So we just add the absolute value of the content offset to the height constraint.

We will also need to keep track of what the original height of the imageview so we can resize it back to the original height once we are done scrolling up.

This logic is again easy to translate into code.

In your view controller add the variable

var originalHeight: CGFloat!

and in viewDidLoad() add the following code

func viewDidLoad(){
...
originalHeight = imageViewHeight.constant
}

so we immediately capture what the original value of the height constraint is.

Next, we add the following code to our scrollviewDidScroll() function

func scrollViewDidScroll(_ scrollView: UIScrollView){  ...

if offset < 0 {
...
//Original height + abs(offset)
imageViewHeight.constant = originalHeight - offset
...
}else { //We are finished scrolling up
//Done streching out the image
imageViewHeight.constant = originalHeight
}
}

Run your code and you should see the following.

Almost there.

We are almost there. We also want the width to scale along with the height. If we change the content mode of the image to aspect fill instead of scale fill then our app should look like this.

Cooler stretchy header

And we are done.

--

--

Eduardo Carrillo

former iOS mobile craftsman. UCSD Math-CS 2019 Full time Software Engineer