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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How to Create Your Own Slide-Out Panel Navigation
25 mins
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.
!
). 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:
- Get a
centerViewController
by pulling it from the storyboard. - 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.
- 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. - Set up the parent-child relationship using
addChild(_:)
anddidMove(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:
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 IBAction
s and IBOutlet
s 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()
.
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.
But too much cuteness can be a dangerous thing! Tap the Kitties button again to hide them!