Curnelious
Curnelious

Reputation: 1

Create my own navigation bar

I have a parent viewController say mainVC. On the bottom it has a tools bar subView - UIView, with buttons.

When I press these buttons, I would like to push/transition between 3 controllers, but I would like to keep this bottom tool bar on mainVC on top all the time.

If I simply push a new controller it will cover my mainVC - hence the tool bar.

How can I get this effect programmatically ?

Upvotes: 0

Views: 60

Answers (2)

Sweeper
Sweeper

Reputation: 271420

One way to do this is to use one view controller with the toolbar as the only subview. You can call this MyToolBarController. Then, add a container view into MyToolBarController.

enter image description here

Container views are controlled by other view controllers, so you can make one of your 3 VCs to control it. When you present a new VC, it should still be presented in the container view. Since this all happens in MyToolBarController, the toolbar will still be there.

Upvotes: 3

Matic Oblak
Matic Oblak

Reputation: 16774

What I usually do is embed a new view controller. Basically you attach an instance of a view controller to another view controller through some view...

You need to call addChildViewController on parent and when transitioning there are willMove and didMove which will trigger appropriate events on your new view controller. Otherwise you need to add the view of the view controller to the target view as a subview.

Check the following which is a stripped version of what I am using:

class ContentControllerView: UIView {

    @IBOutlet weak var parentViewController: UIViewController?
    private(set) var currentController: UIViewController?

    private var addedConstraints: [NSLayoutConstraint] = [NSLayoutConstraint]()

    func setViewController(controller: UIViewController?) {
        guard let parentViewController = parentViewController else {
            print("ContentControllerView error: You need to set a parentViewController to add a new view controller")
            return
        }

        if controller?.view != currentController?.view {

            currentController?.willMove(toParentViewController: nil) // Notify the current controller it will move off the parent
            controller?.willMove(toParentViewController: parentViewController) // Notify the new controller it will move to the parent
            if let controller = controller {
                parentViewController.addChildViewController(controller) // Add child controller
            }
            let toRemove = addedConstraints
            addedConstraints.removeAll()

            controller?.view.translatesAutoresizingMaskIntoConstraints = false // Disable this to add custom constraints
            if let controller = controller {
                self.addSubview(controller.view) // Add as subview

                addedConstraints.append(NSLayoutConstraint(item: self, attribute: .left, relatedBy: .equal, toItem: controller.view, attribute: .left, multiplier: 1.0, constant: 0.0))
                addedConstraints.append(NSLayoutConstraint(item: self, attribute: .right, relatedBy: .equal, toItem: controller.view, attribute: .right, multiplier: 1.0, constant: 0.0))
                addedConstraints.append(NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: controller.view, attribute: .top, multiplier: 1.0, constant: 0.0))
                addedConstraints.append(NSLayoutConstraint(item: self, attribute: .bottom, relatedBy: .equal, toItem: controller.view, attribute: .bottom, multiplier: 1.0, constant: 0.0))

                // Assign new constraints
                self.addConstraints(addedConstraints)
            }
            controller?.view.layoutIfNeeded()
            self.layoutIfNeeded()

            toRemove.forEach { self.removeConstraint($0) }
            self.currentController?.view.translatesAutoresizingMaskIntoConstraints = true

            controller?.view.frame = CGRect(x: 0.0, y: 0.0, width: self.frame.size.width, height: self.frame.size.height)


            self.currentController?.view.removeFromSuperview()
            self.currentController?.didMove(toParentViewController: nil) // Notify the current controller it did move off the parent
            self.currentController?.removeFromParentViewController() // remove the current controller from parrent
            controller?.didMove(toParentViewController: parentViewController) // Notify the new controller it did move to the parent

            self.currentController = controller
            self.superview?.setNeedsLayout()
            (self.superview ?? self).layoutIfNeeded()
        }
    }

    func clear() {
        guard currentController != nil else {
            return
        }

        setViewController(controller: nil)
    }

}

You can use this view in code or in Storyboard. Note that parentViewController MUST be set. So in storyboard you would add a normal UIView above your bar and then set its class to ContentControllerView. Then you ctrl+drag from it to its view controller and select parentViewController.

Then in the code you can call setViewController on it with desired view controller. In your case that is your navigation controller which will then work internally.

The stripped part is I removed everything related to animations. Consequently I am pretty sure there are some extra calls to layout in there left.

Upvotes: 1

Related Questions