6

I have the navigation as:

List of Friends with messages Controller (click compose) -> List of friends to select chatting (select friend) -> Show chat with friend

At present, if within Show chat with friend, and the user selects back, it takes them to the List of friends to select chatting controller. I wish to skip over this controller and, upon back selection, navigate to List of Friends with messages Controller

Note: the List of Friends with messages Controller is an embedded within a tab.

I have attempted using: self.navigationController?.viewControllers.removeLast(), within the segue between the List of friends to select chatting to remove it from the stack. But then after the navigating to Show chat with friend, the back button disappears...

How can I allow the navigation I am describing?

Sauron
  • 6,399
  • 14
  • 71
  • 136
  • You have the right idea, but I would suggest that you modify the `self.navigationController?.viewControllers` array in the `viewWillAppear` functioning your `show chat with friend` view controller. Note that you can't just use `removeLast` in this instance. You will have to remove element `count-2` – Paulw11 Jun 05 '16 at 01:54
  • Use Delegates, Notifications to perform this. – iYoung Jun 05 '16 at 02:59
  • I believe the answer here http://stackoverflow.com/questions/18824186/trying-to-handle-back-navigation-button-action-in-ios will help you. – Axel Jun 05 '16 at 03:54
  • @axel where is the swift translation? – Sauron Jun 05 '16 at 04:16
  • there is in the answers provided – Axel Jun 05 '16 at 04:21

4 Answers4

4

If your goal is to skip second UIViewControllerand pop back to first UIViewController from third UIViewController. Try the following code:

// This count will return how many view controllers are there in navigation stack
let controllersInNavigationCount = self.navigationController?.viewControllers.count

// This will pop to first UIViewController skipping second UIViewController
self.navigationController?.popToViewController(self.navigationController?.viewControllers[controllersInNavigationCount!-2] as! FirstViewController, animated: true)
Ashish Verma
  • 1,776
  • 1
  • 16
  • 27
0

This is a bit tricky. How I would approach this problem is

  • In Show chat with friend View Controller under viewWillAppear I would set a NSUserDefault called pageViewed and set its value to 1.

  • Now in List of friends to select chatting View Controller under viewWillAppear I would check to see if the value of pageViewed NSUserDefault is 1. If it is then set it back to 0 and call make a to remove it from stack.

//Remove from Nav-Stack
[self.navigationController popViewControllerAnimated:YES];

Again its a little tricky but definitely doable.

Sam B
  • 27,273
  • 15
  • 84
  • 121
  • 1
    Use unwindForSegue(unwindSegue: UIStoryboardSegue, towardsViewController: UIViewController) – jjatie Jun 05 '16 at 02:09
  • @jjtalie, An unwind segue has a nicer effect: you don't animate through the intermediate controller on the way to the target controller, however as far as I can tell you can't hook up an unwind segue (or any other action) to a navigation bar's back button. If the op is willing to replace the back button with a custom button, or add an additional button to the interface of the third controller, then the unwind segue seems like a good, easy solution. – 7stud Jun 06 '16 at 19:24
0

Xcode 7.2.1
iOS 9.2
Swift 2.1.1
OSX 10.10.5

The following worked for me:

  1. You can use navigationController?.popViewControllerAnimated(true) in a loop to go skip as many view controllers as you want.

  2. If the ViewController that you want to go back to is the first ViewController in the UINavigationController stack, you can use navigationController?.popToRootViewControllerAnimated(true) to go back to the first view controller.

The app still animates through the skipped ViewController, so you see the skipped View slide by on the way to the target ViewController.

I have attempted using: self.navigationController?.viewControllers.removeLast(), within the segue between the List of friends to select chatting to remove it from the stack. But then after the navigating to Show chat with friend, the back button disappears...

Hmmm...that seems like it should work. The back button information is retrieved from the previous ViewController's navigationItem property. The previous ViewController's navigationItem.backBarButtonItem contains the information for the back button that you see. So, when you removed that ViewController from the stack, why didn't the UINavigationController retrieve the navigationItem.backBarButtonItem info from an earlier ViewController?

It sounds like the destination ViewController may have already gotten ahold of a weak reference to the previous ViewController--the one you removed from the stack. Then, when you removed the previous ViewController from the viewControllers array, the previous ViewController disappeared, and the weak reference was assigned nil--which prevented the destination ViewController from getting the information for the back button; hence no back button was displayed.

AppDelegate.swift:

//
//  AppDelegate.swift
//  NavigationBarExample
//
//  Copyright © 2016 7stud. All rights reserved.
//

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.

        let navController = window!.rootViewController as! UINavigationController
        let friendsController = navController.topViewController as! FriendsTableViewController
        friendsController.friendStore = FriendStore(names: ["Joe", "Cathy", "Bobo"])

        return true
    }

...
...

FriendsTableViewController.swift:

//  FriendsTableViewController.swift
//  NavigationBarExample
//
//
//  Copyright © 2016 7stud. All rights reserved.
//

import UIKit

class FriendsTableViewController: UITableViewController {
    var friendStore: FriendStore!

    override func viewDidLoad() {
        super.viewDidLoad()

        //Prevent TableView from underlapping the status bar:
        let statusBarHeight = UIApplication.sharedApplication().statusBarFrame.height
        let insets = UIEdgeInsets(
            top: statusBarHeight, left: 0, bottom: 0, right: 0
        )
        tableView.contentInset = insets
        tableView.scrollIndicatorInsets = insets
    }

    //MARK: - UITableViewDataSource methods:

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

    override func tableView(tableView: UITableView,
        cellForRowAtIndexPath
        indexPath: NSIndexPath)
        -> UITableViewCell
    {
        let friend = friendStore.allFriends[indexPath.row]

        let cell = tableView.dequeueReusableCellWithIdentifier(
            "UITableViewCell-Default",
            forIndexPath: indexPath
        )
        cell.textLabel?.text = friend.name

        return cell
    }

    //MARK: - Segue:

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        if segue.identifier == "ShowFriend" {
            if let row = tableView.indexPathForSelectedRow?.row {
                let viewController2 = segue.destinationViewController as! ViewController2
                viewController2.friend = friendStore.allFriends[row]
                viewController2.previousTitle = navigationItem.title
            }


        }
    }

}

FriendStore.swift:

//
//  FriendStore.swift
//  NavigationBarExample
//
//  Copyright © 2016 7stud. All rights reserved.
//

import Foundation

class FriendStore {

    var allFriends: [Friend] = []

    init(names: [String]) {
        for name in names {
            allFriends.append(Friend(name: name) )
        }
    }
}

Friend.swift:

//
//  Friend.swift
//  NavigationBarExample
//
//  Copyright © 2016 7stud. All rights reserved.
//

import Foundation

class Friend: NSObject {
    var name: String

    init(name: String) {
        self.name = name

        super.init()
    }

}

ViewController2.swift:

//
//  ViewController2.swift
//  NavigationBarExample
//
//  Copyright © 2016 7stud. All rights reserved.
//

import UIKit

class ViewController2: UIViewController {
    var friend: Friend! {
        didSet {
            navigationItem.title = friend.name
        }
    }

    var previousTitle: String!

    override func viewDidLoad() {
        //Set the correct title for the back button on the next view controller:
        let myBackButtonItem = UIBarButtonItem(
            title: previousTitle,
            style: .Plain,
            target: nil,
            action: nil  //An action specified here won't get called--I
                         //think the NavigationController must overwrite this sometime later.
        )

        navigationItem.backBarButtonItem = myBackButtonItem

    }
}

ViewController3.swift:

//
//  ViewController3.swift
//  NavigationBarExample
//
//  Copyright © 2016 7stud. All rights reserved.
//

import UIKit

class ViewController3: UIViewController {

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)

        let goBackCount = 2

        if let navController = navigationController {
            let viewControllers = navController.viewControllers

            if viewControllers.count >= goBackCount {
                for _ in 1...goBackCount {
                    navController.popViewControllerAnimated(true)
                }

            }
        }
    }
}

A less general way to accomplish the same thing is: if the controller you want to go back to is the root ViewController, i.e. it's the first controller in the UINavigationController stack, then in viewWillDisappear() you can simply call navigationController?.popToRootViewControllerAnimated(true)

Main.storyboard: enter image description here After I created the Table View in the storyboard, I selected the Table View and in the Xcode menu bar, I selected Editor>Embed In>NavigationController. The Navigation Controller is the initial view controller. I also double clicked on the middle of the navigation bar in the Table View and set the title to Friends.

Then I control+dragged from the Prototype Cell in the Table View to View Controller2; from the popup I chose Selection Segue->Show.

Then I control+dragged from the button in View Controller2 to View Controller3; from the popup I chose Action Segue->Show.

7stud
  • 46,922
  • 14
  • 101
  • 127
0

The best way to go back N view controllers is to use the navigationController.viewControllers. When you get to your last viewController you remove the N previous viewControllers like this:

let previousViewControllerIndex = navigationController.viewControllers.count - N navigationController.viewControllers.remove(at: previousViewControllerIndex)

After that when you press go back you will be navigated to the controller you want without seeing the pop animation of the previous view controllers you have removed.

highFly
  • 95
  • 1
  • 5