如何获取推动当前视图的上一个视图控制器?

42
我的应用程序首页有两个 UIButtons,btnIncome 和 btnExpense。按下这些按钮会推出 IncomeVC 和 ExpenseVC,它们都是通过 xib 添加了 UITabBar 的两个 UIViewControllers。tabBar 有 4 个选项卡。选择每个选项卡项目后将同样的四个视图控制器(包含 UITableViews)添加为 IncomeVC 和 ExpenseVC 的子视图,例如 DailyVC、WeeklyVC、MonthlyVC、YearlyVC(即对于收入,有每日、每周等,对于支出也是如此)。(我这样做是因为 IncomeVC 和 ExpenseVC 具有一个 UIButton 和一个 UIView,这对于所有选项卡都是通用的)。
问题是,如果我点击 btnIncome,我必须使用与收入相关的数组填充这些 tableviews,支出则反之。如何找到我从哪个视图控制器中选择了不同的选项卡(我需要从添加为 IncomeVC 和 ExpenseVC 的 4 个视图中获取)。我是否需要为 Income 和 expense 分别创建 8 个不同的视图,每个有 4 个?谢谢。
11个回答

40

Swift 3 中,

if let navController = self.navigationController, navController.viewControllers.count >= 2 {
     let viewController = navController.viewControllers[navController.viewControllers.count - 2]
}

38

你可以像下面的代码一样获取上一个视图控制器:

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

这将显示上一个视图控制器的名称...


2
@Erik 的计数属性始终比索引大1,所以要获得当前 UIViewController 之前的 UIViewController,您必须从计数中减去2。 - Cyprian
1
由于数组越界可能导致崩溃。 - RubenVot
我显然知道,这就是我给你的投票负分的原因。你应该修复你的代码,或者添加注释警告其他人你的代码不安全。另外,顺便说一句,我赞同你对Swift 3的回答。 - RubenVot

21

Swift 中:

let n: Int! = self.navigationController?.viewControllers?.count
let myUIViewController = self.navigationController?.viewControllers[n-2] as! UIViewController

1
直接访问n-2是不安全的,因为如果你是第一个VC,你会遇到错误。也许你可以使用之前的验证。 - baquiax

16

Swift 5.1

(基于先前的答案但更简单)

extension UINavigationController {
    var previousViewController: UIViewController? {
       viewControllers.count > 1 ? viewControllers[viewControllers.count - 2] : nil
    }
}

14

Swift 3

这是之前答案的混合体,可以放进一个扩展中:

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

编辑:

当在viewWillDisappear中获取给定视图控制器的previousViewController,称其为VC1时,VC1已经从导航控制器堆栈中弹出。因此,在这种情况下,上述代码不会直接获取位于VC1正上方的视图控制器(称其为VC2),而是该视图控制器正上方的视图控制器(如果存在)。

为了避免这个问题,我只需要在请求previousViewController时检查VC1是否仍然在堆栈中。以下是更新后的代码:

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
    }
}

该代码假设您发送previousViewController消息的视图控制器要么在导航堆栈的顶部,要么根本不存在。


是否可能出现这样一种情况,即当前查看的视图控制器不在导航堆栈的顶部? - J.beenie

11
如果需要访问上一个视图控制器的原因是为了知道要获取什么数据,我建议在将新的视图控制器推入堆栈之前,将数据传递给它。或者至少传递足够的数据,使得视图控制器知道如何获取正确的数据,例如枚举或常量等。
这可以通过自定义初始化器或属性来完成。请参考这篇博客文章作为示例:"Passing Data Between View Controllers" 如果您正在使用故事板,则可以使用prepareForSegue:sender来传递正确的数据。关于这方面的好教程可以在这里找到:Beginning Storyboards in iOS 5 Part 2

5
UIViewController *previousViewController = [[[self navigationController]viewControllers] objectAtIndex:([viewControllers indexOfObject:self]-1)];

其中self将是当前视图控制器。


4

[viewControllers.count - 2]方法存在缺陷

如果所述的视图控制器不是导航堆栈中的最后一个视图控制器,则[viewControllers.count - 2]会返回错误的结果。

解决方案:

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]
    }
}

3

Swift

extension UINavigationController {
    func getPreviousViewController() -> UIViewController? {
        let count = viewControllers.count
        guard count > 1 else { return nil }
        return viewControllers[count - 2]
    }
}

1
您可以重复使用视图控制器。我建议在按下按钮时,不要推UIViewController(其中包含从IB添加的UITabBar),而是推送包含4个视图控制器(daily、weekly、monthly和yearly)的UITabBarController。
您的UITabBarController可以有一个枚举属性(收入和支出),并且可以根据按下的按钮在推动此UITabBarController时进行分配。从那里,您可以将另一个枚举属性(收入和支出)分配给4个UIViewControllers,以显示每个视图控制器中的正确数据类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接