How to get the previous viewcontroller that pushed my current view
Asked Answered
E

12

42

The home page of my app has UIButtons, btnIncome and btnExpense. Pressing on this buttons pushes IncomeVC and ExpenseVC respectevely,which are two UIViewControllers with UITabBar added via xib. The tabBar have 4 items. Selecting on each tab item adds same four view controllers(which contains UITableViews) as the subview of IncomeVC and ExpenseVC,like for eg, DailyVC,WeeklyVC,MonthlyVC,YearlyVC.(ie,for Income ,there is daily,weekly etc and same for Expense) (I have done like that because the IncomeVC and ExpenseVC have a UIButton and a UIView which is common for all tabs).

So the problem is that, if click the btnIncome I have to populate those tableviews with the arrays related to Income and vice versa for Expense. How can I find from which viewController I selected the different tabs(I need to get it from the 4 Views I added as the subview of IncomeVC and ExpenseVC). Do I have to make 8 different views 4 each for Income and expense ? Thanx.

Edla answered 17/5, 2013 at 12:3 Comment(0)
E
11

If the reason for needing access to the previous view controller is to know what data to get, I would suggest that you instead give the new view controller the data before you push it on the stack. Or at least enough data so that the view controller know how to get the right data, e.g. a enum or constant or something.

This could be done with a custom initializer, or a property. Take a look at this blog post for an example: "Passing Data Between View Controllers"

If you are using a storyboard, you can use prepareForSegue:sender to pass the right data. A good tutorial on that can be found here: Beginning Storyboards in iOS 5 Part 2

Extrude answered 29/5, 2013 at 9:52 Comment(0)
L
40

In Swift 3,

if let navController = self.navigationController, navController.viewControllers.count >= 2 {
     let viewController = navController.viewControllers[navController.viewControllers.count - 2]
}
Lanthorn answered 18/10, 2016 at 5:19 Comment(0)
L
38

You can get the previous viewController like following code,

NSLog(@"%@",[self.navigationController.viewControllers objectAtIndex:self.navigationController.viewControllers.count-2]);

This will displays the previous viewController name...

Lanthorn answered 17/5, 2013 at 12:22 Comment(3)
@Erik count property is always greater by 1 then the index so to get the UIViewController before the current UIViewController you must subtract 2 from the countGemmagemmate
Potential crash due to array out of boundsClaycomb
I obviously know and that's the reason why I downvoted. You should fix your code or add the comment warning other people that your code is insecure. btw, I upvoted you answer for Swift 3Claycomb
V
21

In Swift:

let n: Int! = self.navigationController?.viewControllers?.count
let myUIViewController = self.navigationController?.viewControllers[n-2] as! UIViewController
Vito answered 4/12, 2014 at 15:4 Comment(1)
It's unsafe access directly n-2 because if you are the first VC you will have an error. Maybe you can use an previous validation.Unstudied
E
16

Swift 5.1 🔸

(Based on previous answers but simpler)

extension UINavigationController {
    var previousViewController: UIViewController? {
       viewControllers.count > 1 ? viewControllers[viewControllers.count - 2] : nil
    }
}
Eutrophic answered 4/11, 2019 at 17:41 Comment(0)
L
14

Swift 3

Here is a mashup of the previous answers that can be put into an extension:

extension UIViewController{
   var previousViewController:UIViewController?{
        if let controllersOnNavStack = self.navigationController?.viewControllers, controllersOnNavStack.count >= 2 {
            let n = controllersOnNavStack.count
            return controllersOnNavStack[n - 2]
        }
        return nil
    }
}

Edit:

When fetching the previousViewController of a given view controller, call it VC1, in viewWillDisappear, VC1 is already popped of the Navigation Controller Stack. So in this scenario, the above code does not end up fetching the View controller directly above VC1(call it VC2), but the view controller above VC2 (if it exists).

To avoid this problem I just check if VC1 is still on the stack when previousViewController is requested. Here is the updated code:

extension UIViewController{
    var previousViewController:UIViewController?{
        if let controllersOnNavStack = self.navigationController?.viewControllers{
            let n = controllersOnNavStack.count
            //if self is still on Navigation stack
            if controllersOnNavStack.last === self, n > 1{
                return controllersOnNavStack[n - 2]
            }else if n > 0{
                return controllersOnNavStack[n - 1]
            }
        }
        return nil
    }
}

This code assumes that view controller you are sending the previousViewController message to will either be at the top of the navigation stack or not at all.

Lieutenant answered 21/3, 2017 at 1:17 Comment(1)
Is it possible to have a situation in which the view controller being viewed is not on the top of the navigation stack?Lieutenant
E
11

If the reason for needing access to the previous view controller is to know what data to get, I would suggest that you instead give the new view controller the data before you push it on the stack. Or at least enough data so that the view controller know how to get the right data, e.g. a enum or constant or something.

This could be done with a custom initializer, or a property. Take a look at this blog post for an example: "Passing Data Between View Controllers"

If you are using a storyboard, you can use prepareForSegue:sender to pass the right data. A good tutorial on that can be found here: Beginning Storyboards in iOS 5 Part 2

Extrude answered 29/5, 2013 at 9:52 Comment(0)
M
5
UIViewController *previousViewController = [[[self navigationController]viewControllers] objectAtIndex:([viewControllers indexOfObject:self]-1)];

Where self will be current view controller.

Myriagram answered 23/1, 2014 at 11:21 Comment(0)
H
4

[viewControllers.count - 2] approach is buggy

If stated view controller is not last view controller of the navigation stack,[viewControllers.count - 2] returns incorrect result.

New crash safe solution:

Swift

extension UIViewController {

    var previousViewController: UIViewController? {
        guard 
            let viewControllers = navigationController?.viewControllers,
            let index = viewControllers.firstIndex(of: self),
            index > 0
        else { return nil }
        
        return viewControllers[index - 1]
    }
}
Hooky answered 12/7, 2021 at 11:39 Comment(0)
M
3

Swift

extension UINavigationController {
    func getPreviousViewController() -> UIViewController? {
        let count = viewControllers.count
        guard count > 1 else { return nil }
        return viewControllers[count - 2]
    }
}
Margeret answered 7/6, 2018 at 15:43 Comment(0)
P
1

You can certainly reuse the view controllers. I would suggest instead of pushing UIViewController (which contains UITabBar added from IB) when the button is pressed, you can push UITabBarController which contains 4 View Controllers (daily, weekly, monthly, and yearly).

You UITabBarController could have an enum property (Income and Expense) and this can be assigned when you push this UITabBarController based on the button pressed. From there, you can assign another enum property (Income and Expense) to the 4 UIViewControllers to show the correct type of data in each view controller.

Palomino answered 29/5, 2013 at 9:27 Comment(0)
F
1

Check out this post: How to identify previous view controller in navigation stack Tony is right, you can get the previous VC from the viewControllers array but at index n-2.

Firecracker answered 29/5, 2013 at 9:43 Comment(0)
B
0
func pushedFromViewController() -> UIViewController? {
    if let navController = navigationController {
        return navController.viewControllers.last { $0 != self }
    }
    return nil
}
Bibliophile answered 17/4, 2024 at 21:15 Comment(1)
There are 11 existing answers to this question, including a top-voted answer with over forty votes. Are you certain your solution hasn't already been given? If not, why do you believe your approach improves upon the existing proposals, which have been validated by the community? Offering an explanation is always useful on Stack Overflow, but it's especially important where the question has been resolved to the satisfaction of both the OP and the community. Help readers out by explaining what your answer does different and when it might be preferred.Tenancy

© 2022 - 2025 — McMap. All rights reserved.