dimiss modal and return to presented childViewController in containerView

此生再无相见时 提交于 2019-12-25 14:48:55

问题


I am having a bit of an issue with dismissing a modal view presented from a childviewController in a container view. I have a UINavigationController as the rootViewController (MainNavigationController), and present a modal from one of the childViewControllers from the selectedSegmentIndex 1 (secondViewController). The modal is presented fine, but when I dismiss the modal to go back to the secondViewController(a subclass of HomeController) it returns me back to selectedIndex 0, so not the selectedIndex 1 childViewController it was presented from. I would like the modal to dismiss and return the user back to childViewController it was presented from (the secondViewController) and not return back to selectedIndex 0. Thanks in advance!

// NavigationConroller as rootViewController

class MainNavigationController: UINavigationController {

    var segmentedController: UISegmentedControl!

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        let vc1 = TravelersFeedVC()
        let vc2 = ProfileVC()

        if isLoggedIn() {
            // assume user is logged in
            let homeController = HomeController()
            viewControllers = [homeController]
            homeController.firstViewController = vc1
            homeController.secondViewController = vc2

        } else {
            perform(#selector(showLoginController), with: nil, afterDelay: 0.01)
        }
    }

    fileprivate func isLoggedIn() ->  Bool {
        return UserDefaults.standard.isLoggedIn()
    }

    func showLoginController() {
        let loginController = LoginController()
        present(loginController, animated: true, completion: {
            // perhaps do something here later
        })
    }
}

// HomeController as parentViewController

class HomeController: UIViewController, FBSDKLoginButtonDelegate {

    // child view controllers to put inside content view
    var firstViewController: TravelersFeedVC?
    var secondViewController: ProfileVC?

    private var activeViewController: UIViewController? {
        didSet {
            removeInactiveViewController(inactiveViewController: oldValue)
            updateActiveViewController()
        }
    }

    private func removeInactiveViewController(inactiveViewController: UIViewController?) {
        if let inActiveVC = inactiveViewController {
            // call before removing child view controller's view from hierarchy
            inActiveVC.willMove(toParentViewController: nil)

            inActiveVC.view.removeFromSuperview()

            // call after removing child view controller's view from hierarchy
            inActiveVC.removeFromParentViewController()
        }
    }

    private func updateActiveViewController() {
        if let activeVC = activeViewController {
            // call before adding child view controller's view as subview
            addChildViewController(activeVC)

            activeVC.view.frame = contentView.bounds
            contentView.addSubview(activeVC.view)

            // call before adding child view controller's view as subview
            activeVC.didMove(toParentViewController: self)
        }
    }

    // UI elements
    lazy var contentView: UIView = {
        let tv = UIView()
        tv.backgroundColor = UIColor.purple
        tv.translatesAutoresizingMaskIntoConstraints = false
        tv.layer.masksToBounds = true
        return tv
    }()


    var segmentedController: UISegmentedControl!

    override func viewDidLoad() {
        super.viewDidLoad()

        activeViewController = firstViewController

        checkIfUserIsLoggedIn()

        view.addSubview(contentView)

        setupProfileScreen()

        let items = ["Travelers", "Me"]
        segmentedController = UISegmentedControl(items: items)
        navigationItem.titleView = segmentedController

        segmentedController.tintColor = UIColor.black
        segmentedController.selectedSegmentIndex = 0

        // Add function to handle Value Changed events
        segmentedController.addTarget(self, action: #selector(HomeController.segmentedValueChanged(_:)), for: .valueChanged)

        navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Sign Out", style: .plain, target: self, action: #selector(handleSignOut))
        navigationItem.leftBarButtonItem?.tintColor = UIColor.black

    }


    // reference to collectionViewController
    var travelersFeedVC: TravelersFeedVC!

    func segmentedValueChanged(_ sender:UISegmentedControl!)
    {
        switch segmentedController.selectedSegmentIndex {
        case 0:
            activeViewController = firstViewController

        case 1:
            activeViewController = secondViewController

        default: // Do nothing
            break
        }
    }

// secondViewcontroller in containerView where modal is presented from

class ProfileVC: UIViewController {

// button to present modal
    lazy var placesButton: UIButton = {
        let customButton = UIButton(type: .system)
        customButton.backgroundColor = UIColor.clear
//        customButton.frame = CGRect(x: 150, y: 50, width: 120, height: self.view.frame.height)
        customButton.setTitle("## of Places", for: .normal)
        customButton.titleLabel?.font = UIFont.boldSystemFont(ofSize: 16)
        customButton.setTitleColor(.white, for: .normal)
        customButton.addTarget(self, action: #selector(handleShowPlacesVC), for: .touchUpInside)

        return customButton
    }()

// function to call to present modal
    func handleShowPlacesVC() {
        let placesVC = PlacesTableVC()
        let navigationController = UINavigationController(rootViewController: placesVC)
        present(navigationController, animated: true, completion: nil)
    }

// modal view to dismiss

   override func viewDidLoad() {
        super.viewDidLoad()

        navigationItem.leftBarButtonItem = UIBarButtonItem(title: "back", style: .plain, target: self, action: #selector(handleCancel))

    }

// dismiss modal view to return to secondViewController in childViewController containerView
    func handleCancel() {
        dismiss(animated: true, completion: nil)
    }

回答1:


When closing the modal dialog the viewDidAppear function in MainNavigationController is called. There you set a new homeController with it's childs. This will trigger a viewDidload in the HomeController with setting of firstViewController. Try to set a breakpoint there and you will see it.

I suggest to avoid content creation in viewDidAppear, use viewDidLoad instead.

Another hint: 'dismiss' is defined as: 'Dismisses the view controller that was presented modally by the view controller.' - If you open for instance an alert above your modal vc it closes the alert, not the modal view (self). A correct implementation has to call dismiss on the presenting controller (same controller that opened it): "presentingViewController?.dismiss()" It works in your code because apple has implemented a fallback for the case that nothing is presented, but it's a trap that cause some headache sometime.




回答2:


The chances are that although you're calling present from the child view controller, it isn't in fact handling the presentation. From the Apple docs:

The object on which you call this method may not always be the one that handles the presentation. Each presentation style has different rules governing its behavior. For example, a full-screen presentation must be made by a view controller that itself covers the entire screen. If the current view controller is unable to fulfill a request, it forwards the request up the view controller hierarchy to its nearest parent, which can then handle or forward the request.

Since you're keeping a reference of the active view controller, one solution may be to explicitly set the index upon dismissal.



来源:https://stackoverflow.com/questions/43440127/dimiss-modal-and-return-to-presented-childviewcontroller-in-containerview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!