How to Create Your Own Slide-Out Navigation Panel in Swift

The tutorial uses the CenterViewController as the only view and regardless if you open the right or left SideViewController and select a menu item, the only thing it does is is change the image and labels in the CenterViewController.

How do you handle the for different ViewControllers other then just the CenterViewController!? Meaning if you had a view specifically for a list of videos and another view specifically for maps etc


I guess I might be venting, but I have to say that this tutorial is full of Sh!t
 how can you give praise to this tutorial when it really doesn’t meet anyones use case!? Why would anyone create and app that when you slide out the menu it just changes aspects to the ONLY view in the application!!!

Realistically this would be a good tutorial and would be practical if the author allowed this application to change/swap the entire view (centerview) itself, based on the menu item the user selects!!! Worst off is that the author does not answer or support the tutorial what seems like ever
 but this website keeps advertising to buy their tutorials and/or books, which if this is the indiction of the stuff you get if you purchase anything from these instructions (non practical examples and tutorials for real life applications) you are just getting ripped off!!!

Question, have you been able to swap different views based on the menu item selected in the slide menu?!

Wow! Really, after the way you have spoken to the Ray Wenderlich team on this page you ask for help?

I think I’ll pass on giving you any more detail than this: yes, you can do what you require.

May I suggest that you do a lot more work on your own and refrain from blaming others for your lack of knowledge? Perhaps this isn’t the site for you; there are plenty of other places you can go for the help you need and, I think it is worth pointing out to you, this article isn’t designed to help you with the complete solution you are looking for. A developer often needs to use several different resources to fill gaps of knowledge and fulfill a requirement.

There are lots of people out here who are willing to help you, but you need to demonstrate a willingness to learn from others and investigate things for yourself. Expecting spoon-fed solutions won’t help you achieve your goals and insulting people who try to help you will put others off (as you have put me off).

A good hint to the wrongness of your position on this article is something that you have already noticed for yourself; count up the number of people who think this is a great article and then count up the number of people who think this is a bad article and work out the percentages. Once you’ve done that, maybe you’ll realise that you might be missing something


Has anyone worked out that solution or can provide some insight for implementing it

This is a fantastic tutorial as it is very simple and elegant. The tutorial does exactly what it intends to do - demonstrate how to create a sliding side panel. While adding navigation would have been a nice feature, I believe menu navigation is out of the scope of the tutorial intention. The tutorial does provide the delegate for any needed navigation in the center controller. Here is how I implemented navigation. For my scenario, I used an enumerator to store the sliding menu item text displayed in the SidePanelViewController. Then in the center controller SidePanelViewControllerDelegate menuItemSelected function, I switched on the values to launch the needed viewController. For this example, I only provided 1 navigation and then a message box for easy testing.

I changed animal/Animal to menuItem/MenuItem. I have created another viewController in the main storyboard with a storyboardID of ToyLineViewController

// possible navigation items
enum MenuItemText: String {
    case ToyLine = "Show by Toy Line"
    case Series = "Show by Series"
    case Items = "Show all Items"
}

let kToyLineViewControllerStoryboardID = "ToyLineViewController"

extension CenterViewController: SidePanelViewControllerDelegate {

func menuItemSelected(_ menuItem: MenuItem) {
   titleLabel.text = menuItem.menuItemText
    
    delegate?.collapseSidePanels()

    switch menuItem.menuItemText {
    case MenuItemText.ToyLine.rawValue:
        if let resultController = storyboard!.instantiateViewController(withIdentifier: kToyLineViewControllerStoryboardID) as? ToyLineViewController {
            present(resultController, animated: true, completion: nil)
        }

    // Note:  put other real navigation cases here
    // Note:  default is used as a placeholder until other viewControllers are built
    default:
        let alert = UIAlertController(title: "Alert", message: titleLabel.text, preferredStyle: UIAlertControllerStyle.alert)
        alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.default, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
}
}

I am trying to convert it to Swift 3 on Xcode 8. getting the following error. I am new to iOS so might be pretty basic error.

In SidePanelViewController.swift:13:8: Method cannot be a member of an @objc protocol because the type of the parameter cannot be represented in Objective-C

The error is on the method declaration in: -
@objc
protocol SidePanelViewControllerDelegate {
func animalSelected(_ animal: Animal)
}

I was able to solve this by subclassing NSObject in the Animal class. Essentially, the object cannot be exposed to objective-c without being subclassed as an NSObject.

Thanks for this great tutorial. My question is why we want to destroy the left and right panel everytime we dismiss them. Is it better just hide them. Most of the menu is state information which won’t change much.

I made some changes, but not able to uploaded.

Here are the changes on ContainerViewController

class ContainerViewController: UIViewController {

let centerPanelExpandedOffset: CGFloat = 60.0
var centerNavigationControl : UINavigationController!
var centerViewController: CenterViewController!
var currentState: SlideOutState = .BothCollapsed {

    didSet {
        let shouldShowShadow = currentState != .BothCollapsed
        showShadowForCenterViewController(shouldShowShadow: shouldShowShadow)
    }
}


lazy var leftViewController : SidePanelViewController = {

    var leftController = UIStoryboard.leftViewController()
    leftController?.animals = Animal.allCats()
    leftController?.delegate = self.centerViewController
    var frame = leftController!.view.frame
    leftController?.view.frame = CGRect(x: -frame.size.width, y: frame.origin.y, width: frame.size.width - self.centerPanelExpandedOffset, height: frame.size.height)
    leftController?.view.isHidden = true
    self.view.addSubview(leftController!.view)
    self.addChildViewController(leftController!)
    leftController?.didMove(toParentViewController: self)
    
    return leftController!
}()


lazy var rightViewController: SidePanelViewController = {
    
    var rightController = UIStoryboard.rightViewController()
    rightController?.animals = Animal.allDogs()
    rightController?.delegate = self.centerViewController
    var frame = rightController!.view.frame
    rightController?.view.frame = CGRect(x: frame.size.width, y: frame.origin.y, width: frame.size.width - self.centerPanelExpandedOffset, height: frame.size.height)
    rightController?.view.isHidden = true
    self.view.addSubview(rightController!.view)
    self.addChildViewController(rightController!)
    rightController?.didMove(toParentViewController: self)
    
    return rightController!
}()


override func viewDidLoad() {
    super.viewDidLoad()
    
    
    
    centerViewController = UIStoryboard.centerViewController()
    centerViewController.delegate = self
    
    centerNavigationControl = UINavigationController(rootViewController: centerViewController)
    view.addSubview(centerNavigationControl.view)
    addChildViewController(centerNavigationControl)
    
    centerNavigationControl.didMove(toParentViewController: self)
    
    //let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(recognizer:)))
    //centerNavigationControl.view.addGestureRecognizer(panGestureRecognizer)

}

}

extension ContainerViewController: CenterViewControllerDelegate {

func collapseSidePanels() {
    switch (currentState) {
    case .RightPanelExpanded:
        currentState = .BothCollapsed
        animateRightPanel(shouldExpand: false)
    case .LeftPanelExpanded:
         currentState = .BothCollapsed
         animateLeftPanel(shouldExpand: false)
    default:
        break
    }
}

func toggleLeftPanel() {
    switch currentState {
    case .BothCollapsed:
        animateLeftPanel(shouldExpand: true)
    case .RightPanelExpanded:
        animateRightPanel(shouldExpand: false)
        animateLeftPanel(shouldExpand: true)
    case .LeftPanelExpanded:
        break
    }
}

func toggleRightPanel() {
    switch currentState {
    case .BothCollapsed:
        animateRightPanel(shouldExpand: true)
    case .LeftPanelExpanded:
        animateLeftPanel(shouldExpand: false)
        animateRightPanel(shouldExpand: true)
    case .RightPanelExpanded:
        break
    }
}

func animateLeftPanel(shouldExpand: Bool) {
    if (shouldExpand) {
        currentState = .LeftPanelExpanded
        self.leftViewController.view.isHidden = false
        animatePanelXPosition(targetView: self.leftViewController.view, targetPosition: 0)
    } else {
        animatePanelXPosition(targetView: self.leftViewController.view, targetPosition: -self.leftViewController.view.frame.size.width){
            finished in self.leftViewController.view.isHidden = true}
    }
}

func animateRightPanel(shouldExpand: Bool) {
    if (shouldExpand) {
        currentState = .RightPanelExpanded
        self.rightViewController.view.isHidden = false
        animatePanelXPosition(targetView: self.rightViewController.view, targetPosition: centerPanelExpandedOffset)
    } else {
        animatePanelXPosition(targetView: self.rightViewController.view, targetPosition: +self.rightViewController.view.frame.size.width){
            finished in self.rightViewController.view.isHidden = true}

    }
}

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

func showShadowForCenterViewController(shouldShowShadow: Bool) {
    if (shouldShowShadow) {
        centerNavigationControl.view.layer.shadowOpacity = 0.6
    } else {
        centerNavigationControl.view.layer.shadowOpacity = 0.0
    }
}

}

private extension UIStoryboard {
class func mainStoryboard() → UIStoryboard { return UIStoryboard(name: “Main”, bundle: Bundle.main) }

class func leftViewController() → SidePanelViewController? {
return mainStoryboard().instantiateViewController(withIdentifier: “LeftViewController”) as? SidePanelViewController
}

class func rightViewController() → SidePanelViewController? {
return mainStoryboard().instantiateViewController(withIdentifier: “RightViewController”) as? SidePanelViewController
}

class func centerViewController() → CenterViewController? {
return mainStoryboard().instantiateViewController(withIdentifier: “CenterViewController”) as? CenterViewController
}

Hello,

Nice Tutorial. !! but one question.
why the leftViewController and rightViewController are optional and we perform add and remove?
can’t we just hide and show, because I want to highlight the previous selected option when leftViewController is opened again

This tutorial is more than six months old so questions are no longer supported at the moment for it. We will update it as soon as possible. Thank you! :]