Breaking down the Twitter Profile Screen Part II: Stretchy Header (iOS Swift 4)
In the second part of the series we are going to be building out a sticky image navbar.
We will fix the highlighted cell another time.
This is the second part of the series for implementing features for Twitter’s profile screen. The first article can be found here.
Prereqs:
- You have read and understood my first article
- Knowledge of fundamental iOS concepts (UIViewController lifecycle, UIViews, etc.)
- Autolayout fundamentals
Strategy:
- Add tableview to profile screen and set up constraints
2. Add a headerview to tableview (Not a section header)
3. Add imageview to headerview
4. Set up constraints for imageview
5. Make imageview sticky/stretchy like last tutorial
6. Detect when navigation bar bottom reaches bottom of image view
7. When the navigation bar bottom reaches bottom of imageview then we will move down the image view
Note: Most of the stuff is the same as the last article except that we are using a tableview and parts 6 and 7.
Implementation:
Add tableview to profile screen and set up constraints
Add the tableview to the scene and setup the Autolayout constraints as follows. Make sure to pin the table view’s Top, Leading, Bottom, Trailing with 0 space to the UIView’s corresponding edges. Do not pin to the Safe Area. If you do you will get a bug if you embed the view controller into a navigation controller. You will see white space instead of your content under your nav bar if you pin to the Safe Area.
Embed scene into Navigation view controller
We will embed our table view into a navigation controller and we get a free navigation bar from it.
Adding header view to table view
Not a section header but an actual header for the tableview.
Now we are going to add an imageview and it’s neccesary constraints in exactly the same way as the previous article
Setting up constraints for image view inside header view
Making imageview sticky/stretchy
Read last tutorial for instructions.
Run you code to make sure it works. If everything works correctly then you should see the following
Now we are pretty much at the end of the last tutorial everything past this point should be new.
It is difficult to see with the navbar in the way but there is a small bug.(Make the navbar transparent and you will be able to see this.)We need to adjust our baseline(what we define to be the top of the screen)in our viewdidscroll function. So instead of checking if offset < 0, we should check for if offset < baseline. In the last tutorial our baseline was 0 because we did not have a navigation bar. But now that we have a navigation bar this makes the “new top” of the screen to be <navbar’s height> units up. In other words if the navigation bar has height 64, then our content offset by default will have -64 when the screen is left alone without scroll.
So you should make the following changes.
Create an instance variable that will hold the navigation bar’s height.
Extract the navigation bar’s height from the viewDidLoad() function
Then create a constant inside your scrollViewDidScroll() function that will act as your baseline/top of screen value
Now we replace the below code
if offset < 0 {//code}
with
1.) if offset < baseline {//code}
and
imageview.height.constant = originalHeight - offset
becomes
2.) imageviewHeight.constant = originalHeight + abs (base - offset)
finally
else {
imageviewTop.constant = 0}
becomes
3. else {
imageviewTop.constant = base
//other code}
- This code just detects if we move past the top. As explained earlier the top is defined to be -1* navigation bar height
- This code just stretches the imageview by how much the screen moved upward. Units moved upward = difference between initial position (base) and current position (offset)
- When we finish scrolling we need to put the imageview back at the original top before we started scrolling which is just -1*navbar height
Don’t forget to change the imageview’s content mode to aspect fill as well.
Generalizing sticky/stretchy code
Note: This is not safe code. If you were to navigate to this view controller from a view controller not embedded in a navigation controller this code would crash. (Can you see why?) You could make it more safe by using optionals but for brevity I decided to skip on the safe code.
Your app should now look like this.
Now time to make the navigation bar sticky with the background of the image in place.
Next thing we will do is make our navigation bar transparent. The way we will do this is by giving our navigation controller a custom class and modifying the navigation bar in our custom class.
Transparent Navigation Bar
Run your app again.
We will actually make this view controller as a datasource for our table view in order to display cells on the screen so we can actually test scrolling.
Setting up tableview stubs
We want to make it look like the navigation bar has the bottom of the image as the background when we scroll down far enough. We will achieve this effect by moving down the imageview as soon as the bottom of the navigation bar hits the bottom of the image view.
Now we need to figure when the navbar bottom hits the bottom of the imageview. This is when the offset + navbar height = base + imageview height. So we keep track to see if this event has occured. If it has then we will give the illusion of the image being stuck to the navigation bar by just sliding down the image view.
If this event has occured, then we will move down imageview porportionally with how far we have scrolled down.
How far we scrolled down = difference between nav bar offset and bottom of the image
After we are done scrolling we want to make sure to put the imageview in the correct spot.
This logic is implemented in the below code.
Setting up navigation bar background image illusion
Run your app for the last time and …
we have our desired effect.