Vadim
Vadim

Reputation: 335

Show back button without navigation view controller

I have the scheme: UITabBarViewController (with 3 tabs).
In all that tabs I don't want to show navigation menu on top. And from the first tab, I want to push another view controller from button click that will have "back" button (and top toolbar with "cancel")

I tried some ways - in storyboard with push segue - no back button. Probably because i don't have navigation view controller, so my navigation stack is empty.

Programmatically:

let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)

let nextViewController = storyBoard.instantiateViewController(withIdentifier: "AddCoinTableViewController") as! AddCoinTableViewController
self.present(nextViewController, animated:true, completion:nil)

If I embed tabs in navigation controller, then I have top toolbar (which I don't want).

Any ideas how to make it?

Upvotes: 4

Views: 6585

Answers (3)

Sharad Paghadal
Sharad Paghadal

Reputation: 2154

You can't achieve navigation functionality without using UINavigationController. I mean you have to do all animation kind of stuff on your own, and I think that's not a good idea. Rather than that, you can use UINavigationController, and if you don't want to show navigationBar at some viewController, than do as follows for those view controllers.

override func viewWillApear() {
    super.viewDidLoad()
    self.navigationController?.isNavigationBarHidden = true
}

override func viewWillDisappear(animated: Bool) {
    self.navigationController?.isNavigationBarHidden = false
}

Upvotes: 10

Brandon
Brandon

Reputation: 23498

The below code will allow you to create your own Navigation handling class and have the "push" "pop" animation that UINavigationController has.. You can create a new project, copy paste the below into ViewController.swift and see for yourself..

Now you can give any UIViewController navigation controller abilities..

import UIKit

class NavigationHandler : NSObject, UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning, UINavigationBarDelegate {
    private var isPresenting: Bool = false
    private weak var controller: UIViewController?

    init(controller: UIViewController) {
        super.init()

        self.controller = controller
        controller.transitioningDelegate = self

        let navigationBar = UINavigationBar()
        controller.view.addSubview(navigationBar)
        NSLayoutConstraint.activate([
            navigationBar.leftAnchor.constraint(equalTo: controller.view.leftAnchor),
            navigationBar.rightAnchor.constraint(equalTo: controller.view.rightAnchor),
            navigationBar.topAnchor.constraint(equalTo: controller.view.safeAreaLayoutGuide.topAnchor)
        ])
        navigationBar.translatesAutoresizingMaskIntoConstraints = false
        navigationBar.delegate = self

        let item = UINavigationItem(title: controller.title ?? "")
        let barButton = UIBarButtonItem(title: "Back", style: .done, target: self, action: #selector(onBackButton(button:)))

        item.leftBarButtonItems = [barButton]
        navigationBar.setItems([item], animated: true)
    }

    func position(for bar: UIBarPositioning) -> UIBarPosition {
        return .topAttached
    }

    @objc
    private func onBackButton(button: UIBarButtonItem) {
        self.controller?.dismiss(animated: true, completion: nil)
    }

    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        self.isPresenting = true;
        return self;
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        self.isPresenting = false;
        return self;
    }

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.25;
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let duration = self.transitionDuration(using: transitionContext)
        let fromController = transitionContext.viewController(forKey: .from)
        let toController = transitionContext.viewController(forKey: .to)
        let containerView = transitionContext.containerView

        if self.isPresenting {
            let frame = fromController!.view.frame
            containerView.addSubview(toController!.view)
            toController?.view.frame = CGRect(x: frame.origin.x + frame.width, y: frame.origin.y, width: frame.width, height: frame.height)

            UIView.animate(withDuration: duration, animations: {

                fromController?.view.frame = CGRect(x: frame.origin.x - frame.size.width, y: frame.origin.y, width: frame.size.width, height: frame.size.height)

                toController?.view.frame = frame

            }, completion: { (completed) in
                transitionContext.completeTransition(true)
            })
        }
        else {
            let frame = fromController!.view.frame
            containerView.insertSubview(toController!.view, at: 0)
            toController?.view.frame = CGRect(x: frame.origin.x - frame.size.width, y: frame.origin.y, width: frame.size.width, height: frame.size.height)

            UIView.animate(withDuration: duration, animations: {

                fromController?.view.frame = CGRect(x: frame.origin.x + frame.width, y: frame.origin.y, width: frame.width, height: frame.height)

                toController?.view.frame = frame

            }, completion: { (completed) in
                transitionContext.completeTransition(true)
            })
        }
    }
}

View Controllers for testing:

class ViewController : UIViewController {

    private var navigationHandler: NavigationHandler?

    override func viewDidLoad() {
        super.viewDidLoad()

        let button = UIButton(type: .custom)
        button.setTitle("Push Controller", for: .normal)
        button.setTitleColor(UIColor.red, for: .normal)
        button.layer.borderColor = UIColor.black.cgColor
        button.layer.borderWidth = 1.0
        button.layer.cornerRadius = 5.0
        button.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(button)
        NSLayoutConstraint.activate([
            button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
            button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
            button.heightAnchor.constraint(equalToConstant: 45.0),
            button.widthAnchor.constraint(equalToConstant: 150.0)
        ])

        button.addTarget(self, action: #selector(onPush(button:)), for: .touchUpInside)
    }

    @objc
    private func onPush(button: UIButton) {
        let child = ChildViewController()
        self.navigationHandler = NavigationHandler(controller: child)
        self.present(child, animated: true, completion: nil)
    }
}

class ChildViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.view.backgroundColor = UIColor.blue
    }
}

Upvotes: 2

Gustavo Vollbrecht
Gustavo Vollbrecht

Reputation: 3256

You can embed the navigation controller at your first tab controller (or any you want), and hide it at the controllers you don't want on their viewDidLoad like this:

self.navigationController?.isNavigationBarHidden = true

Doing this, you will be able to see the back Button at the controllers you pushed and didn't hide the navigationBar.

Make sure you push the controller using the navigation controller like this:

self.navigationController?.pushViewController(YOUR VIEW CONTROLLER, animated: true)

Upvotes: 2

Related Questions