iOS

Parallax Header with Page Tabbar Controller tutorial in iOS

If you are an iOS developer, you should have heard about parallax header and page tabbar controller. There are many tutorials and libraries on these feature, but just implementing individually. What about combine these 2 features together? In this post I’ll guide how to combine parallax header with page tabbar controller easily.

Set up page tabbar controller

First, download source code in this link.

Open Xcode Workspace in path: start/ParallaxHeaderPagingViewControllerDemo.xcworkspace

The source code in this project is pretty simple. It contains a screen with a UITableView display an ascending ordered number list. Run the project to see for yourself.

Screenshot of start project

Now we’ll integrate page tabbar controller by using library Parchment. Open Podfile and insert the following code after line use_frameworks!:

pod 'Parchment'
Add Parchment

Open terminal, cd to the path [root]/start, then run the following command:

pod install

In Xcode, create a new UIViewController with name as ParchmentExampleViewController with the following code:

import UIKit
import Parchment

class ParchmentExampleViewController: UIViewController {

    @IBOutlet weak var pagingContainer: UIView!
    var viewControllers: [TableViewController] = []
    var pagingViewController: PagingViewController!
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Do any additional setup after loading the view.
        // 1
        viewControllers = [
            TableViewController(count: 20),
            TableViewController(count: 1),
            TableViewController(count: 10),
            TableViewController(count: 100),
        ]
        // 2
        for (index, viewController) in viewControllers.enumerated() {
            viewController.title = String(index)
        }

        // 3
        let pagingViewController = PagingViewController(viewControllers: viewControllers)
        pagingContainer.addSubview(pagingViewController.view)
        pagingViewController.view.translatesAutoresizingMaskIntoConstraints = false
        pagingViewController.view.topAnchor.constraint(equalTo: pagingContainer.topAnchor).isActive = true
        pagingViewController.view.bottomAnchor.constraint(equalTo: pagingContainer.bottomAnchor).isActive = true
        pagingViewController.view.leftAnchor.constraint(equalTo: pagingContainer.leftAnchor).isActive = true
        pagingViewController.view.rightAnchor.constraint(equalTo: pagingContainer.rightAnchor).isActive = true
        pagingViewController.didMove(toParent: self)
        self.pagingViewController = pagingViewController
    }
}

What you just did:

  1. Create an array of TableViewController with different number of items.
  2. Config title for each item in array viewControllers.
  3. Set up PagingViewController and add it as subview of pagingContainer.

Open Main.storyboard, delete Table View Controller Scene. Then add a new UIViewController and subclass it as ParchmentExampleViewController.

Next, add a UIView into ParchmentExampleViewController. This view will be used as container for page tabbar controller. Config its contraints as follow:

  • Top space to safe area top with 0 px.
  • Leading, Trailing, Bottom space to parent view’s edges with 0 px.

If you config correctly, Xcode should show like this:

Config auto layout for page view controller container
Config auto layout for page view controller container

Select the view, connect its IBOutlet to variable pagingContainer:

Connect IBOutlet
Connect IBOutlet

Select ParchmentExampleViewController, on the right sidebar open attributes inspector and tick on Is Initial View Controller.

Set ParchmentExampleViewController as initial view controller
Set ParchmentExampleViewController as initial view controller

 Now run the project, you can see the screen is displaying a page tabbar controller:

Page Tabbar Controller Screenshot

We have finished set up page tabbar controller. Next we’ll integrate parallax header into our demo.

Integrate parallax header

In this tutorial we’ll use HPParallaxHeader to implement parallax header. First, open Podfile and add the following code after line pod 'Parchment':

pod 'HPParallaxHeader'

Then, run command in terminal:

pod install

Open Main.storyboard, add a UIScrollView with top, bottom, leading and trailing constraint to super view’s corresponding edges. This scroll view will be used to display parallax header. In Size Inspector, deselect option Content Layout Guides. If you config correctly, Xcode should display as follow:

Add UIScrollView

Next, move Paging Container view into Scroll View and add the following constaint:

  • Top, bottom, leading and trailing equal to super view’s edges.
  • Width and height equal to super view’s width and height.

Xcode should show as follow:

Move Paging Containter into Scroll View

Switch to file ParchmentExampleViewController.swift, add the following line below var pagingViewController: PagingViewController!:

@IBOutlet weak var scrollView: HPScrollView!

In method viewDidLoad, add the following code:

// 1
scrollView.parallaxHeader.load(nibName: "StarshipHeader", bundle: nil, options: [:])
// 2
scrollView.parallaxHeader.height = 300
// 3
scrollView.parallaxHeader.mode = .fill

Switch to Main.storyboard, select scroll view, subclass it as HPParallaxHeader.

Subclass scroll view as HPScrollView
Subclass scroll view as HPScrollView

Connect its IBOutlet to variable scrollView of ParchmentExampleViewController.

Connect scroll view IBOutlet

Now, you can run the project. The parallax header is now working properly with page tabbar view controller. But there is some wrong: the tab bar is overlap with safe area which makes you cannot tap on it.

Tab bar overlap safe area
Tab bar overlap safe area

We’ll fix this problem by adding the following code after viewDidLoad:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    scrollView.parallaxHeader.minimumHeight = view.safeAreaInsets.top
}

In the above block code we set minimum height of parallax header equal to safe area insets top to make the tab bar avoid safe area. Run the project and you can see the tab bar position is now below safe area top which is good.

Now try to scroll the list to end and … Oops! The bottom of the list is now beyond the bottom of the screen which is kind of weird.

Parallax Header with Page Tab bar Controller bottom space bug
Parallax Header with Page Tab bar Controller bottom space bug 

It turns out since we previously set up pagingContainer’s height equal super view height, when we set parallax header minimum height the pagingContainer’s bottom is push below super view’s bottom. We’ll fix this by subtracting pagingContainer’s height with parallax header’s minium height.

In ParchmentExampleViewController.swift, add the following code below @IBOutlet weak var pagingContainer: UIView!:

@IBOutlet weak var pagingContainerHeightConstraint: NSLayoutConstraint!

Swich to Main.storyboard, select Parchment Example Scene, connect IBOutlet from pagingContainer’s height contraint with variablepagingContainerHeightConstraint just created above.

Select height constraint of pagingContainer
Select height constraint of pagingContainer
Create IBOutlet from height constraint of pagingContainer
Connect IBOutlet from height constraint of pagingContainer

Back to ParchmentExampleViewController.swift, add the following code at the end of viewDidAppear:

pagingContainerHeightConstraint.constant =  -(scrollView.parallaxHeader.minimumHeight)

Run the project, now everything works perfectly!

Parallax Header with Page Tab bar Controller Finish
Parallax Header with Page Tab bar Controller Finish

Wraps Up

So we finally finish integrate parallax header with page tab bar controller into our application. It is pretty easy right? With the combination of HPParallaxHeader and Parchment it takes you only 5 – 10 minutes to complete a task that may cost you 1 – 2 day if do it yourself. Hope this article helps. If you have any questions please let me know in the comment section below.

Leave a Reply

Your email address will not be published. Required fields are marked *