
I've created a sample project for this question. You can see the output in the above image. basically what I did is created a wrapper class around the Sidebar and then I used it whenever I want :)
Sidebar
import UIKit
protocol SidebarDelegate {
func sidbarDidOpen()
func sidebarDidClose(with item: Int?)
}
class SidebarLauncher: NSObject{
var view: UIView?
var delegate: SidebarDelegate?
var vc: NavigationViewController?
init(delegate: SidebarDelegate) {
super.init()
self.delegate = delegate
}
func show(){
let bounds = UIScreen.main.bounds
let v = UIView(frame: CGRect(x: -bounds.width, y: 0, width: bounds.width, height: bounds.height))
v.backgroundColor = .clear
let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "NavigationController") as! NavigationViewController
v.addSubview(vc.view)
vc.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
vc.view.topAnchor.constraint(equalTo: v.topAnchor),
vc.view.leadingAnchor.constraint(equalTo: v.leadingAnchor),
vc.view.bottomAnchor.constraint(equalTo: v.bottomAnchor),
vc.view.trailingAnchor.constraint(equalTo: v.trailingAnchor, constant: -60)
])
vc.delegate = self
v.isUserInteractionEnabled = true
v.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTapGesture(_:))))
self.view = v
self.vc = vc
UIApplication.shared.keyWindow?.addSubview(v)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut], animations: {
self.view?.frame = CGRect(x: 0, y: 0, width: self.view!.frame.width, height: self.view!.frame.height)
self.view?.backgroundColor = UIColor.black.withAlphaComponent(0.5)
}, completion: {completed in
self.delegate?.sidbarDidOpen()
})
}
@objc func handleTapGesture(_ sender: UITapGestureRecognizer){
closeSidebar(option: nil)
}
func closeSidebar(option: Int?){
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut], animations: {
if let view = self.view{
view.frame = CGRect(x: -view.frame.width, y: 0, width: view.frame.width, height: view.frame.height)
self.view?.backgroundColor = .clear
}
}, completion: {completed in
self.view?.removeFromSuperview()
self.view = nil
self.vc = nil
self.delegate?.sidebarDidClose(with: option)
})
}
}
extension SidebarLauncher: NavigationDelegate{
func navigation(didSelect: Int?) {
closeSidebar(option: didSelect)
}
}
NavigationController
import UIKit
protocol NavigationDelegate{
func navigation(didSelect: Int?)
}
class NavigationViewController: UIViewController{
@IBOutlet weak var buttonLaunchVC: UIButton!
@IBOutlet weak var buttonSecondViewController: UIButton!
@IBOutlet weak var buttonThirdViewController: UIButton!
var delegate: NavigationDelegate?
override func viewDidLoad() {
super.viewDidLoad()
[buttonLaunchVC,buttonSecondViewController,buttonThirdViewController].forEach({
$0?.addTarget(self, action: #selector(didSelect(_:)), for: .touchUpInside)
})
}
@objc func didSelect(_ sender: UIButton){
switch sender {
case buttonLaunchVC:
delegate?.navigation(didSelect: 0)
case buttonSecondViewController:
delegate?.navigation(didSelect: 1)
case buttonThirdViewController:
delegate?.navigation(didSelect: 2)
default:
break
}
}
@IBAction func CloseMenu(_ sender: Any) {
delegate?.navigation(didSelect: nil)
}
}
ViewController
class ViewController: UIViewController {
@IBAction func OpenMenu(_ sender: Any) {
SidebarLauncher(delegate: self ).show()
}
}
extension ViewController: SidebarDelegate{
func sidbarDidOpen() {
print("Sidebar opened")
}
func sidebarDidClose(with item: Int?) {
guard let item = item else {return}
print("Did select \(item)")
switch item {
case 0:
break
case 1:
let v = UIStoryboard.main.SecondVC()
present(v!, animated: true)
case 2:
let v = UIStoryboard.main.ThirdVC()
present(v!, animated: true)
default:
break
}
}
The main area of interest is SidebarLauncher class
what it does: when you call the show() method. it creates a UIView then it adds it to keywindow (i.e. your current View) and after that It adds the NavigationController.
To setup communication with the sidebar, I've created two Protocols
- SidebarDelegate:
Sidebar delegate is the main protocol through which you get to know if the user has selected any ViewController or not.
- NavigationDelegate:
this protocol is used for communication between the wrapper and the navigation controller. When user tap any button. it informs the wrapper class about it.
The wrapper class has a method closeSidebar which then closes the sidebar and inform the Controller class that sidebar is closed with option.
In sidebarDidClose, you can decide what to do with the selection made by user.
I was in a bit of hurry which is why I used Int whereas you should consider using struct or class whichever suits your need to identify which ViewController to open.
https://github.com/sahilmanchanda2/SidebarTest