A iOS screen with different side screens visible on axis X and Y

It's important to note that Snapchat utilizes two axes of navigation. The first axis (X) is horizontal and relatively simple to incorporate, as discussed in Part 1 of this tutorial. The second axis (Y) is vertical and more challenging to develop, but I found it to be an enjoyable process. In fact, the second part of this tutorial will focus on how to create this vertical axis and serve as the foundation for mastering Snapchat's complete navigation system.

To recap, this is how the navigation should function:

  1. From the center panel the user should be able to swipe up and down to extra panels
  2. The top panel seems to appear from underneath scroll view
  3. The bottom panel overlaps the camera/central layer when appearing but below the navigation buttons
  4. Bonus problem: the camera layer (central panel) acts as a background for the left and right panel — while scrolling horizontally, they cover the central panel rather than pushing the view away.

So how do we do it? In my opinion adding second scroll view is not an option, due to the custom transitioning of the top and bottom panel. That’s why I think this is a great opportunity to use UIPanGestureRecognizer! By measuring changes in touch position on the main screen, we can calculate the Panel y-axis position and with proper positioning on the z-axis we will resolve issues with custom transitioning.

See the image below:

N/A
My interpretation of Snapchat’s layers

Therefore we can divide our Panel into 4 different layers:

  1. Red — top panel container
  2. Blue — bottom panel container
  3. Picture — center panel container, that’s the place for camera controller
  4. Yellow, green and transparent panel — scroll view implemented in previous part with one major change. Since we moved central panel under scroll view, the transparent part stays empty. Here we can put UIViewController, which recognizes pan gestures.

Now that we have a plan, let’s dive into the code.

  

We’ll also need an extension to fit controllers into containers

  

Then we setup the UI in viewDidLoad once again with following changes

  

The code is pretty straightforward; just remember to add layers of subviews in proper order.

The fun begins when we start implementing PanController. We have to add a gesture recognizer which measures touch translation in our view on y axis. We should start by adding following lines to viewDidLoad:

  

This delegate of gesture recognizer protects us from measuring translations other than vertical ones.

  

Now when we add the recognizer, we should handle it somehow. Our aim is to animate the container positions on main view controller. Access to them can be provided by using a delegate pattern:

  

We will implement delegate methods in main view controller later, for now let’s start work on handling PanGesture.

  

These variables are important for setting the container center and prediction of user swiping. Transition to top and bottom panel can only take place when the user swipes with correct velocity, or he drags the view to the desired distance from its origin.

  

Now let’s break down what we have done here:

  1. Calculation of translation and velocity in view. If the velocity reaches the threshold, we set the direction of user gesture
  2. Here we need to pay some extra attention:
  3. In Snapchat, while dragging the view vertically, we have some resistance; the bigger the touch translation, the slower the animation moves. Let’s do some math:

N/A
  • You can see three different plots of y functions. The red one corresponds to y = x, a linear relationship. It means, that with every change in touch translation, we have an equal change in the screen transition. This is exactly that what we wanted to avoid…
  • We need a function which grows at the beginning and with a changing `x` so it becomes nearly constant. Just like moving the center panel in the app. It starts smooth, but with dragging we increase its resistance for changing position. The green plot represents y = root(x), which is somehow that what we wanted to achieve. But we can do even better. The blue plot shows what we’re looking for: a plot of the log10(x) function.
  • After dividing it by 10 and subtracting from 1 we have a range of values (1 -> 0). This range is nothing more than representation of how big percent of translation we should add to original view center to simulate scroll resistance in our app.

3. Setting the container origins

4. Changing the position of containers by setting a new center with parallax effect.

5. Checking the position of containers, if it’s big enough, or we ended swiping with proper velocity, we’re handling this event with animated transition.

  

Go to the main view controller, now is the time to add panController.delegate = self and implement delegate.

  

The two axes of navigation are now ready to test.

Like in Snapchat, going back from top and bottom panel by swipe gestures is disabled, so you can add buttons which implement present(_ panel:) delegate to avoid a dead end.

In the original app, you can also navigate by using buttons. We will add them and in third part of this tutorial.

Read the other parts of this tutorial