4

I'm trying to set up a login screen (ViewController) that leads - after a successful login - to a user list (UserTableViewController) that is itself part of a navigation controller. On the subsequent screens like the UserTableViewController it should be possible to logout. This would bring the user back to the initial login screen ViewController. I'm really struggling connecting those screens the right way. It must be said that I don't have a lot of experience with the different kinds of segues and/or delegates so, with some research done, I went for some trials:

  1. A successful login on ViewController triggers a modal-segue to the navigation controller (that itself leads to the UserTableViewController)
  2. The UserTableViewController has a logout button that triggers another modal-segue back to the ViewController. I took these modal segues because first, I didn't want to have a hierarchy that leads to an automatically created back-button or similar and second, I didn't want to have any "troubles" between these two screens, one having a navigation controller while the other one doesn't.

...it looks like that's not a way to go. Some things get mixed up after one loop and screens are changing the wrong way. I guess a modal-segue-circle is not possible as there has to be parent and a child at least. Next trial:

  1. A successful login on ViewController triggers a modal-segue / push-segue to the navigation controller (that itself leads to the UserTableViewController)
  2. To return to the login screen I implemented a delegate instead of another segue, triggered when tapping "logout" - here I'm facing the problem that I can't set up the UserTableViewController's delegate in preparingForSegue on ViewController properly. At this point segue.destinationViewController cannot be downcasted to UserTableViewController but only to NavigationController what doesn't allow me to set up the delegate at the destination (UserTableViewController).

The third trial was to do the same like in the second approach but implementing a segue from ViewController to UserTableViewController directly. That may work but now I don't have any navigation bar anymore at my UserTableViewController...!

Of course I could go for a manual fix in the third solution like inserting a stand-alone navigation bar but neither way seems to be efficient. Therefore I'd very very thankful for some hints, highlighting what I misunderstood (completely) on one side and showing a good way of doing it on the other side. Thanks a lot for any help!

EDIT: I could try to set the navigation controller as initial view controller and then just let the login screen ViewController being presented/dismissed by the UserTableViewController - is that a practical way or are there widely known best practices for those login view scenario?

Just a visual help: enter image description here

alexeis
  • 2,152
  • 4
  • 23
  • 30

3 Answers3

1

Please consider using an "unwind" segue. In the ViewController create a function as follows:

@IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
    yourLogoutCode()
}

In UserTableViewController control-drag from the Logout button to the exit symbol of UserTableViewController which is the third button at the top, and select unwindToThisViewController (or whatever name you selected in ViewController).

Now when you click on the Logout button you will return to ViewController and execute the unwindToThisViewController in which you can put your logout code.

Here is an example:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func yourLogoutCode() {
        println("Logging Out ...")
    }

    @IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
        self.yourLogoutCode()
    }


}

And the UserTableViewController:

import UIKit
var selectedRow = -1
class UserTableViewController: UITableViewController {
    var firstArray = ["Item1","Item2","Item3","Item4"]
    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()

    }

    // MARK: - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return firstArray.count
    }


    let nameOfCell = "Cell"
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier(nameOfCell, forIndexPath: indexPath) as UITableViewCell
        cell.textLabel!.text = firstArray[indexPath.row]
        return cell
    }

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        selectedRow = indexPath.row

    }


}

Notice that the whole connection from the Logout button to the unwind is buried inside the Storyboard. You can confirm that all this is happening by reviewing the Connections Inspector.

Syed Tariq
  • 2,878
  • 3
  • 27
  • 37
0

This post appears to address exactly the issue you are facing, if I understand correctly.

Also, not meaning to nitpick, but it's "modal", not "modular" :)

secondcitysaint
  • 1,158
  • 12
  • 20
  • Thanks for the input, I keep trying! Anyway I'm very curious if there is a common used way for dealing with a login screen which is, from a practical perspective, the initial view controller but that is not part of a navigation stack. As one logs out it returns - I'm wondering what kind of relationships within this cycle is best... In addition I'd like to know how to implement a delegate in such a situation, where a navigation controller "blocks the downcasting". @Nicky – alexeis Sep 12 '14 at 21:23
  • I believe this navigation actually shouldn't be a responsibility of either of these two controllers, but rather some coordinator object responsible for the application flow. What if you have a scenario which requires you to log the user out deep in the navigation controller's hierarchy? E.g., their token expires and you need them to login once again. – secondcitysaint Sep 12 '14 at 21:35
  • Anyway, the preferred approach is letting the presenting controller know (via delegation or some other way) that the presented controller has finished its job, so it could handle the dismissal. After all, the controller being further in the navigation shouldn't really be aware of how exactly it appeared on the screen. – secondcitysaint Sep 12 '14 at 21:39
0

I think that could be an acceptable way of doing it:

  1. Login view (ViewController) is initial View Controller
  2. In case of successful login: Modal-segue from login view to the navigation controller that handles subsequent views
  3. Enabling dismiss-segues in Swift by implementing a sub class of UIStoryboardSegue (needed to be written like this: @objc(DismissSegue) class DismissSegue: UIStoryboardSegue { ) that is overriding its perform() to (sourceViewController.presentingViewController!!).dismissViewControllerAnimated(true, completion: nil)
  4. Dismiss-segue from subsequent views (in my case UserTableViewController) back to the login view
  5. Implement the logout-action within prepareForSegue on each of the View Controllers which allows the user to logout

Still, if anyone knows a better practice, I'm happy to hear!

alexeis
  • 2,152
  • 4
  • 23
  • 30