Jim
Jim

Reputation: 1470

Why does this UITableViewController dismiss modally?

I have a UITableViewController that, when presented from one screen, is presented with the standard 'show' segue from right to left, and when presented from another screen (a UIViewController), is presented modally from the bottom. I got this to work properly with the help I got from a question I asked a few months ago (has screenshots).

The key was creating the segue from the UINavigationController of my Settings screen to the shared UITableViewController instead of creating it from the UITableViewCell. Strangely though, even though it presents correctly from right to left, dismissing it closes it modally (top to bottom).

I'm making the presenting table view controller a delegate of the UITableViewController it's presenting so it will handle the dismissal. Here's the protocol and extension it implements (Swift 2.3):

protocol DismissalDelegate : class {
    func selectionDidFinish(controller: UIViewController)
}

extension DismissalDelegate where Self: UIViewController {
    func selectionDidFinish(viewController: UIViewController) {
        self.dismissViewControllerAnimated(true, completion: nil)
    }
}

And I set it in the segue defined in the presenting controller:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if segue.identifier == "goToLifts" {
        let destination = segue.destinationViewController as! LiftSelectionTableViewController
        destination.dismissalDelegate = self
    } else {
        return
    }
}

The presented table view controller calls delegate?.selectionDidFinish(self) when the user makes a selection (in didSelectRowAtIndexPath):

 override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
        guard (dismissalDelegate != nil ) else {
            return
        }
        dismissalDelegate?.selectionDidFinish(self)
    }

That calls this method in the presenting table view controller:

func selectionDidFinish(controller: LiftSelectionTableViewController) {
    self.dismissViewControllerAnimated(true, completion: nil)
}

I've looked through the APIs for presenting view controllers and haven't been able to find anything that exposes options to control this. The dismiss(animated:completion:) API even says it's for dismissing a view controller presented modally, but I don't see anything else having to do with dismissal.

How can I get this thing to dismiss the same way it's presented when it's presented from my UITableViewController (right to left, and back) but keep the modal behavior when presented from the other view (a UIViewController)?

Upvotes: 1

Views: 601

Answers (1)

Jeffery Thomas
Jeffery Thomas

Reputation: 42598

I'm a little confused here, it looks like you are using the delegate because the presenting view controller should know how LiftSelectionTableViewController got presented.

So in the table view controller, you would have

func selectionDidFinish(viewController: UIViewController) {
    self.navigationController?.popViewControllerAnimated(true)
}

In the other view controller, you should have

func selectionDidFinish(viewController: UIViewController) {
    self.dismissViewControllerAnimated(true, completion: nil)
}

If I'm wrong and you can't know how the view controller was presented, then I would try checking to see if the top view controller on the navigation controller is presented view controller. Pop the view controller if it is, dismiss the view controller if it isn't.

func selectionDidFinish(viewController: UIViewController) {
    if self.navigationController?.topViewController == viewController {
        self.navigationController?.popViewControllerAnimated(true)
    } else {
        self.dismissViewControllerAnimated(true, completion: nil)
    }
}

Upvotes: 1

Related Questions