3

There is a library I really enjoy that you could find here (https://github.com/pixyzehn/MediumMenu).

Essentially it does the following:

MediumMenu Demo

The menu in the back is actually a UIViewController. If we delve into the source code, we'll find the following very important code:

public init(items: [MediumMenuItem], forViewController: UIViewController) {
    self.init()
    self.items = items
    height = screenHeight - 80 // auto-calculate initial height based on screen size
    frame = CGRect(x: 0, y: 0, width: screenWidth, height: height)
    contentController = forViewController
    menuContentTableView = UITableView(frame: frame)
    menuContentTableView?.delegate = self
    menuContentTableView?.dataSource = self
    menuContentTableView?.showsVerticalScrollIndicator = false
    menuContentTableView?.separatorColor = UIColor.clearColor()
    menuContentTableView?.backgroundColor = menuBackgroundColor
    addSubview(menuContentTableView!)

    if panGestureEnable {
        let pan = UIPanGestureRecognizer(target: self, action: #selector(MediumMenu.didPan(_:)))
        contentController?.view.addGestureRecognizer(pan)
    }

    let menuController = UIViewController()
    menuController.view = self

   UIApplication.sharedApplication().delegate?.window??.rootViewController = contentController
   UIApplication.sharedApplication().delegate?.window??.insertSubview(menuController.view, atIndex: 0)
}

The first few lines aren't really important, but the very last lines (the ones dealing with the UIApplication.sharedApplication().delegate?.window?? are the key to this library working, but they also limit the library.

According to those lines, we're making the UIViewController that we want the menu for to be the rootViewController and then we add the menu view to the window at the 0 index (I'm guessing this is what puts it in the back, behind the contentController).

The problem with the library:

The library only works if you want the menu on the initial view controller. In a sense, only if the contentController is already the rootViewController. It'll actually function for me in my app, but I can't segue back to my original UIViewController because it isn't in the hierarchy anymore.

I have a scenario where I have a Login View Controller, and when you successfully login, I segue you to my UINavigationController on which I want the menu. The first clear sign of an issue is that I get the complaint "Warning: Attempt to present UINavigationController on LoginViewController whose view is not in the window hierarchy!" Obviously, it isn't in the window hierarchy because I'm reassigning the rootViewController.

I don't know how to fix this. Is there a way to have this functionality work when the UIViewController I want the menu on isn't the initial view controller?

David
  • 7,028
  • 10
  • 48
  • 95

3 Answers3

3

Short answer is: Yes.

This can be implemented with by assigning a UIViewControllerTransitioningDelegate to the ViewController containing the list-menu and implementing the interactionControllerForPresentation() and interactionControllerForDismissal() methods. You don't have to touch the UIWindow (directly).

You could just change this part about the existing library, but from the looks of it it's just a TableView with a fancy transition. You could easily implement this yourself.

Check out Apple's API Reference on interactive transitions and this tutorial by Ray Wenderlich for a hands on example.

Aerows
  • 760
  • 6
  • 22
2

So it is clear your scenario is:

  1. rootViewController = a UINavigationController
  2. the rootViewController of UINavigationController is the LoginController
  3. When someone login succeed, the navigationController push(or segue) to a menuController

or

  1. rootViewController is the LoginController
  2. When someone login succeed, it will present the menuController

So the key point is to get a menuController and not set it to the rootViewController of the app.

Here is the code which i made some change to fit your requirement. https://github.com/Moonsownner/MediumMenu-master

What did i do:

  1. MediumMenu actually is a UIView, we have to create a controller to contain it so that to make the transition. So i make a new class named MediumMenuController. And you can see i move function showMenu into MediumMenuController. So if someone want to show the menu manually, the target must be a MediumMenuController not the previous NavigationController. And the MediumMenuController will contain your business controller which you send in into the init function of MediumMenuController.
class MediumMenuController: UIViewController {

    let menuView: MediumMenu
    let child: UIViewController

    init(items: [MediumMenuItem], childController: UIViewController){
        self.menuView = MediumMenu(items: items, forViewController: childController)
        self.child = childController
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(menuView)
        addChildViewController(child)
        self.view.addSubview(child.view)
        child.view.frame = UIScreen.mainScreen().bounds
        child.didMoveToParentViewController(self)

    }

    func showMenu() {
        menuView.show()
    }

}
  1. I delete the code in MediumMenu.

let menuController = UIViewController(); menuController.view = self UIApplication.sharedApplication().delegate?.window??.rootViewController = contentController UIApplication.sharedApplication().delegate?.window??.insertSubview(menuController.view, atIndex: 0)

and create a new controller named MediumMenuController to add the MediumMenu and the other controllers views to it.

Maybe it is not clear by saying above. The code can show you the detail. Good luck.

jhd
  • 1,243
  • 9
  • 21
  • Your answer should show the key code additions, not deletions alone, because eventually yhat link will break and this answer will be meaningless – Wain Sep 21 '16 at 06:46
2

You can achieve it by following steps.

(i) Make LoginViewController as your rootViewController.

(ii) When user signs in successfully, redirect user to Home Screen. That is your first screen that you want to show after successfully logged in.

(iii) You can make a parent class of UIViewControllers where you want to show the menu.

(iv) After that, when you initialize any UIViewController, parent class will call public init(items: [MediumMenuItem], forViewController: UIViewController) method and in that you can replace last two lines with below code. self.view.insertSubview(menuController.view, atIndex: 0)

(v) On tapping sign out button, you can simply dismiss presented view controller, so LoginViewController will always be there.

Hope this helps!

Bharat Nakum
  • 647
  • 6
  • 18