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
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Me and My Shadow

When the left panel is open, notice how it’s right up against the center view controller. It would be nice if there were a bit more of a distinction between them. How about adding a shadow?

Still in ContainerViewController.swift, add the following method to the end of the extension:

func showShadowForCenterViewController(_ shouldShowShadow: Bool) {
  if shouldShowShadow {
    centerNavigationController.view.layer.shadowOpacity = 0.8
  } else {
    centerNavigationController.view.layer.shadowOpacity = 0.0
  }
}

This adjusts the opacity of the navigation controller’s shadow to make it visible or hidden. You’ll implement a didSet observer to add or remove the shadow whenever the currentState property changes.

Scroll to the top of ContainerViewController.swift and change the currentState declaration to:

var currentState: SlideOutState = .bothCollapsed {
  didSet {
    let shouldShowShadow = currentState != .bothCollapsed
    showShadowForCenterViewController(shouldShowShadow)
  }
}

The didSet closure executes whenever the property’s value changes. If either of the panels is visible, it shows the shadow.

Build and run the app again. This time when you tap Kitties, check out the sweet new shadow! Looks better, huh?

Slide Out Navigation in Swift - Kitties with shadows

Up next, adding the same functionality but for the right side, which means… puppies!

Puppies to the Right…

To add the right panel view controller, simply repeat the steps for adding the left view controller.

Open ContainerViewController.swift and add the following property below leftViewController:

var rightViewController: SidePanelViewController?

Next, locate toggleRightPanel() and add the following implementation:

let notAlreadyExpanded = (currentState != .rightPanelExpanded)

if notAlreadyExpanded {
  addRightPanelViewController()
}

animateRightPanel(shouldExpand: notAlreadyExpanded)

Next, add the following below toggleRightPanel():

func addRightPanelViewController() {
  guard rightViewController == nil else { return }

  if let vc = UIStoryboard.rightViewController() {
    vc.animals = Animal.allDogs()
    addChildSidePanelController(vc)
    rightViewController = vc
  }
}

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

This code is almost an exact duplicate of the code for the left panel except, of course, for the differences in method and property names and the direction. If you have any questions about it, you can always review the explanation from the previous section.

Just as before, the IBActions and IBOutlets are already connected in the storyboard for you. Similar to Kitties, Puppies is hooked up to an IBAction method named puppiesTapped(_:). This button controls the sliding of the center panel to reveal the right side panel.

Switch to CenterViewController.swift and add the following line to puppiesTapped(_:):

delegate?.toggleRightPanel()

Again, this is the same as kittiesTapped(_:), except it’s toggling the right panel instead of the left.

Time to see some puppies!

Build and run the app again to make sure everything is working. Tap Puppies. Your screen should look like this:

slide-out navigation panel

Looking good, right? But remember, you don’t want to expose yourself to the cuteness of puppies for too long, so tap that button again to hide them away.

You can now view both kitties and puppies, but it would be great to be able to view a bigger picture of each one, wouldn’t it? MORE CUTENESS :]

Pick an Animal, Any Animal!

The kitties and puppies are listed within the left and right panels. These are both instances of SidePanelViewController, which simply contains a table view.

Open SidePanelViewController.swift to take a look at the SidePanelViewControllerDelegate protocol. A side panel’s delegate can be notified via this method whenever an animal is tapped. Time to use it!

In SidePanelViewController.swift, add the following property at the top of the class, underneath the table view IBOutlet:

var delegate: SidePanelViewControllerDelegate?

Then, fill in the implementation for tableView(_:didSelectRowAt:) within the UITableViewDelegate extension:

let animal = animals[indexPath.row]
delegate?.didSelectAnimal(animal)

If there’s a delegate set, this will tell it the user has selected an animal. Currently, there’s no delegate! It would make sense for CenterViewController to be the side panel’s delegate as it can then display the selected animal photo and title.

Open CenterViewController.swift to implement the delegate protocol. Add the following extension beneath the existing class definition:

extension CenterViewController: SidePanelViewControllerDelegate {
  func didSelectAnimal(_ animal: Animal) {
    imageView.image = animal.image
    titleLabel.text = animal.title
    creatorLabel.text = animal.creator
    delegate?.collapseSidePanels()
  }
}

This method simply populates the image view and labels in the center view controller with the animal’s image, title and creator. Then, if the center view controller has a delegate of its own, you tell it to collapse the side panel so you can focus on the selected item.

collapseSidePanels() doesn’t do anything, though! Open, ContainerViewController.swift and add the following implementation to collapseSidePanels():

switch currentState {
case .rightPanelExpanded:
  toggleRightPanel()
case .leftPanelExpanded:
  toggleLeftPanel()
case .bothCollapsed:
  break
}

The switch statement in this method simply checks the current state of the side panels and collapses whichever one is open.

Now, locate addChildSidePanelController(_:) and add the following to the bottom of the method:

sidePanelController.delegate = centerViewController

In addition to what it was doing previously, the method will now set the center view controller as the side panels’ delegate.

That should do it!

Build and run the app. View kitties or puppies and tap on one of the cute little critters. The side panel should collapse itself again and you should see the details of the animal you chose.

Slide Out Navigation in Swift - Puppy Details

Move Your Hands Back and Forth

The navigation bar buttons are great, but most apps also allow you to swipe to open the side panels. Adding gestures to your app is surprisingly simple. Don’t be intimated; you’ll do fine!

Open ContainerViewController.swift again. First, make this class conform to UIGestureRecognizerDelegate by adding the following extension above the UIStoryboard extension:

// MARK: Gesture recognizer

extension ContainerViewController: UIGestureRecognizerDelegate {
  @objc func handlePanGesture(_ recognizer: UIPanGestureRecognizer) {
  }
}

Next, locate viewDidLoad(). Add the following to the end of the method:

let panGestureRecognizer = UIPanGestureRecognizer(
  target: self, 
  action: #selector(handlePanGesture(_:)))
centerNavigationController.view.addGestureRecognizer(panGestureRecognizer)

This creates a UIPanGestureRecognizer, assigns self as the target and handlePanGesture(_:) as the selector to handle any detected pan gestures.

By default, a pan gesture recognizer detects a single touch with a single finger, so it doesn’t need any extra configuration. You just need to add the newly created gesture recognizer to centerNavigationController view.

Note: Refer to our UIGestureRecognizer with Swift Tutorial for more information about gesture recognizers in iOS.

Didn’t I tell you it’d be simple? There’s only one move remaining in your slide-out navigation panel routine. The gesture recognizer calls handlePanGesture(_:) when it detects a gesture. So your last task for this tutorial is to implement the method.

Add the following block of code to handlePanGesture(_:) (it’s a big one!):

// 1
let gestureIsDraggingFromLeftToRight = (recognizer.velocity(in: view).x > 0)

// 2
switch recognizer.state {
// 3
case .began:
  if currentState == .bothCollapsed {
    if gestureIsDraggingFromLeftToRight {
      addLeftPanelViewController()
    } else {
      addRightPanelViewController()
    }
    showShadowForCenterViewController(true)
  }

// 4
case .changed:
  if let rview = recognizer.view {
    rview.center.x = rview.center.x + recognizer.translation(in: view).x
    recognizer.setTranslation(CGPoint.zero, in: view)
  }

// 5
case .ended:
  if let _ = leftViewController,
    let rview = recognizer.view {
    // animate the side panel open or closed based on whether the view
    // has moved more or less than halfway
    let hasMovedGreaterThanHalfway = rview.center.x > view.bounds.size.width
    animateLeftPanel(shouldExpand: hasMovedGreaterThanHalfway)
  } else if let _ = rightViewController,
    let rview = recognizer.view {
    let hasMovedGreaterThanHalfway = rview.center.x < 0
    animateRightPanel(shouldExpand: hasMovedGreaterThanHalfway)
  }

default:
  break
}

Here's what this does:

  1. The pan gesture recognizer detects pans in any direction, but you're only interested in horizontal movement. First, you set up the gestureIsDraggingFromLeftToRight Boolean to check for this using the x component of the gesture velocity.
  2. There are three states that need to be tracked: UIGestureRecognizerState.began, UIGestureRecognizerState.changed and UIGestureRecognizerState.ended. Use a switch to handle each accordingly.
  3. .began: If the user starts panning and neither panel is visible, show the correct panel based on the pan direction and make the shadow visible.
  4. .changed: If the user is already panning, move the center view controller's view by the amount the user has panned
  5. .ended: When the pan ends, check whether the left or right view controller is visible. Depending on which one is visible and how far the pan has gone, perform the animation.

You can move the center view around, show and hide the left and right views using a combination of these three states, as well as the location, velocity and direction of the pan gesture.

For example, if the gesture direction is right, then show the left panel. If the direction is left, then show the right panel.

Build and run the app again. At this point, you should be able to slide the center panel left and right, revealing the panels underneath.