How to Create Your Own Slide-Out Panel Navigation

This easy tutorial will help you add the popular slide-out navigation panels to your apps using Swift 4, Xcode 10 and iOS 12. By Brody Eller.

Leave a rating/review
Download materials
Save for later
Share
Update note: Brody Eller updated this tutorial for iOS 12, Xcode 10, and Swift 4. Tammy Coron wrote the original.

In this tutorial, you’ll build a slide-out panel navigation, which is a popular alternative to using a plain UINavigationController or a UITabBarController for app navigation. The slide-out navigation panel allows users to slide content on or off screen.

Use the Download Materials button found at the top or bottom of this tutorial to get what you’ll need for this tutorial. The following animation shows what you’re going to build in this tutorial.

The slide-out navigation panel design pattern lets developers add permanent navigation to their apps without taking up valuable screen real estate, since users can choose to reveal the navigation at any time, while still seeing their current context.

Getting Started

In this tutorial, you’ll take a less-is-more approach so you can apply the slide-out navigation panel technique to your own apps with relative ease.

Open the project from the SlideOutNavigation-Starter folder, called SlideOutNavigation.xcodeproj, and take a look at how it’s organized. In addition to the view controllers, there’s also an Asset Catalog called Assets.xcassets containing all the adorable kitten and puppy images you’ll use in the app.

Here’s the overall structure of this app:

  • ContainerViewController is where the magic happens! This is the view controller that handles things like animations and swiping between the center view controller and the left and right panels. It’s responsible for holding references to all the other necessary view controllers.
  • CenterViewController is the center panel view controller.
  • SidePanelViewController serves as both the left and right side panel view controllers.

You can find the views for the center, left and right view controllers in Main.storyboard. So, feel free to take a look at the whole project.

Now that you’re familiar with the structure of the app, it’s time to start at square one—the center panel.

Finding Your Center

First order of business is placing CenterViewController inside the ContainerViewController as a child view controller.

Open ContainerViewController.swift. Locate viewDidLoad() and add the following properties right above it:

var centerNavigationController: UINavigationController!
var centerViewController: CenterViewController!

These two properties will hold both the centerViewController and its parent navigation controller.

Note: These are implicitly-unwrapped optionals (as denoted by the !). They have to be optional because their values won’t be initialized until after init() completes, but they can be implicitly unwrapped because you know they’ll be initialized by the time you use them. If they aren’t, then it’s a programmer error and you want to know about it when you test the app.

At the bottom of the file, you’ll see a class extension for UIStoryboard containing a handful of static methods for more convenient loading of specific view controllers from the app’s storyboard. You’ll take advantage of these methods to populate the properties you just created.

Add the following code inside viewDidLoad(), beneath the call to super:

// 1
centerViewController = UIStoryboard.centerViewController()
// 2
centerViewController.delegate = self

// 3
centerNavigationController = UINavigationController(rootViewController: centerViewController)
view.addSubview(centerNavigationController.view)
addChild(centerNavigationController)

// 4
centerNavigationController.didMove(toParent: self)

Don’t worry about the compiler error on the second line, you’ll take care of that shortly.

There’s some fun stuff going on in this short method. Here’s what you’re doing:

  1. Get a centerViewController by pulling it from the storyboard.
  2. Set the current view controller as the center view controller’s delegate so the center view controller can notify its container when to show and hide the left and right side panels.
  3. Create a navigation controller to contain the center view controller so you can push views to it and display bar button items in the navigation bar. Then, add the navigation controller’s view to ContainerViewController‘s view.
  4. Set up the parent-child relationship using addChild(_:) and didMove(toParent:).

Awesome! Now to take care of the error, modify this class so it implements CenterViewControllerDelegate.

Add the following class extension to ContainerViewController below the UIStoryboard extension at the bottom of the file (this also includes a number of empty methods you’ll fill out later):

// MARK: CenterViewController delegate

extension ContainerViewController: CenterViewControllerDelegate {
  func toggleLeftPanel() {
  }

  func toggleRightPanel() {
  }

  func collapseSidePanels() {
  }
}

Implementing these methods makes this class conform to CenterViewControllerDelegate.

Now is a good time to check your progress. Build and run the app. You should see something similar to the screen below:

Slide Out Navigation in Swift main screen

Yes, those buttons at the top will eventually bring you kitties and puppies. What better reason could there be for creating sliding navigation panels? But to get your cuteness fix, you’ve got to start sliding. First, to the left!

Kittens to the Left of Me…

You’ve created your center panel, but adding the left view controller requires a different set of steps.

To expand the left panel, the user will tap on the Kitties button in the navigation bar. Open CenterViewController.swift to get started on implementing that.

In the interests of keeping this tutorial focused on the important stuff, the IBActions and IBOutlets are pre-connected for you in the storyboard. However, to implement your DIY slide-out navigation panel, you need to understand how the buttons are configured.

Notice there are already two IBAction methods, one for each of the buttons. Find kittiesTapped(_:) and add the following implementation to it:

delegate?.toggleLeftPanel()

As previously mentioned, the method is already hooked up to the Kitties button. This uses optional chaining to call toggleLeftPanel() only if delegate has a value.

You can see the definition of the delegate protocol at the bottom. As you’ll see, there are methods called toggleLeftPanel(), toggleRightPanel() and collapseSidePanels(). If you remember, when you set up the center view controller instance earlier, you set its delegate as the container view controller. Time to go and implement toggleLeftPanel().

Note: For more information on delegate methods and how to implement them, please refer to Apple’s Developer Documentation. If you’re interested in the delegate pattern, and other code design patterns, then take a look at our book Design Patterns by Tutorials.

Open ContainerViewController.swift and add an enum to the top of ContainerViewController:

enum SlideOutState {
  case bothCollapsed
  case leftPanelExpanded
  case rightPanelExpanded
}

You’ll use this to keep track of the current state of the side panels, so you can tell whether neither panel is visible or one of the left or right panels is visible.

Next, add two more properties below your existing centerViewController property:

var currentState: SlideOutState = .bothCollapsed
var leftViewController: SidePanelViewController?

These will hold the current state of the side panels and the left side panel view controller itself:

You initialize currentState to .bothCollapsed — that is, neither of the side panels is visible when the app first loads. The leftViewController property is an optional, because you’ll be adding and removing the view controller at various times, so it might not always have a value.

Next, add the implementation for toggleLeftPanel():

let notAlreadyExpanded = (currentState != .leftPanelExpanded)

if notAlreadyExpanded {
  addLeftPanelViewController()
}

animateLeftPanel(shouldExpand: notAlreadyExpanded)

First, this method checks whether the left side panel is already expanded. If it’s not already visible, then you call a method which adds the panel to the view hierarchy. Then, it calls another method which animates it to its “open” position. If the panel is already visible, then it animates the panel to its “closed” position.

Next, you need to add the code to add the left panel to the view hierarchy. Add the following just below toggleLeftPanel():

func addLeftPanelViewController() {
  guard leftViewController == nil else { return }

  if let vc = UIStoryboard.leftViewController() {
    vc.animals = Animal.allCats()
    addChildSidePanelController(vc)
    leftViewController = vc
  }
}

This code first checks to see if the leftViewController property is nil. If it is, then it creates a new SidePanelViewController and sets its list of animals to display — in this case, cats!

Next, add the implementation for addChildSidePanelController(_:) at the bottom of the extension:

func addChildSidePanelController(_ sidePanelController: SidePanelViewController) {
  view.insertSubview(sidePanelController.view, at: 0)

  addChild(sidePanelController)
  sidePanelController.didMove(toParent: self)
}

This method adds the child view controller to the container view controller. This is the same as adding the center view controller earlier. It simply inserts its view — in this case it’s inserted at z-index 0, which means it will be below the center view controller — and adds it as a child view controller.

Add the following constant below your other properties at the top of ContainerViewController:

let centerPanelExpandedOffset: CGFloat = 90

This value is the width, in points, of the center view controller left visible once it has animated off screen. 90 points should do it.

Next, back in the CenterViewControllerDelegate extension, add the following below addLeftPanelViewController():

func animateLeftPanel(shouldExpand: Bool) {
  if shouldExpand {
    currentState = .leftPanelExpanded
    animateCenterPanelXPosition(
      targetPosition: centerNavigationController.view.frame.width 
        - centerPanelExpandedOffset)
  } else {
    animateCenterPanelXPosition(targetPosition: 0) { _ in
      self.currentState = .bothCollapsed
      self.leftViewController?.view.removeFromSuperview()
      self.leftViewController = nil
    }
  }
}

This method first checks whether it’s been told to expand or collapse the side panel. If it should expand, then it sets the current state to indicate the left panel is expanded and calls a method to animate the center panel so it’s open. Otherwise, it animates the center panel closed, removes its view and sets the current state to indicate it’s closed.

Now add this method above addChildSidePanelController(_:):

func animateCenterPanelXPosition(
    targetPosition: CGFloat, 
    completion: ((Bool) -> Void)? = nil) {
  UIView.animate(
    withDuration: 0.5,
    delay: 0,
    usingSpringWithDamping: 0.8
    initialSpringVelocity: 0,
    options: .curveEaseInOut,
    animations: {
      self.centerNavigationController.view.frame.origin.x = targetPosition
    }, 
    completion: completion)
}

This is where the actual animation happens. It moves the center view controller’s view to the specified position with a nice spring animation. The method also takes an optional completion closure, which it passes on to the UIView animation. You can try tweaking the duration and spring damping parameters if you want to change the appearance of the animation.

Build and run the app.

Tap on Kitties in the navigation bar. The center view controller should slide over — whoosh! — and reveal the Kitties menu underneath. D’aww, look how cute they all are.

Slide Out Navigation in Swift - Kitties

But too much cuteness can be a dangerous thing! Tap the Kitties button again to hide them!