0

So I have 3 view controllers: TableViewController, A, and B. The user is able to navigate to any view controller from any view controller.

When the user goes back and forth between A, and B view controllers I want them to be pushed onto the nav. stack. When the "home" button is pressed, I would like for the view controllers to all be popped back to the TableViewController using popToViewController, not popToRootViewController (for reasons).

I have partly working code that pops the last visited view controller, but now all the ones in between.

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

    if indexPath.row == 0 {
        if let navController = self.navigationController {
            for controller in navController.viewControllers {
                if controller is TableViewController {
                    navController.popToViewController(controller, animated: true)
                    break
                }
            }
        }
    } else {

        let vcName = identities[indexPath.row]
        let viewController = storyboard?.instantiateViewController(withIdentifier: vcName)
        self.navigationController?.pushViewController(viewController!, animated: true)
    }

}

I'm not sure why all the view controllers aren't being popped.

Code I use to check what's being pushed and popped:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(true)

    if self.isMovingToParentViewController {
        print("A is pushed")
    }
}

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

    if self.isMovingFromParentViewController {
        print("A is popped")
    }
}

I'm also checking increase in memory.

I will provide more code/info in needed.

Any help would be greatly appreciated.

  • Try adding a `break` after the call to `popToViewController`. You don't want to keep looking after you find the first match. – rmaddy Jul 05 '17 at 19:07
  • If the idea behind the for loop is to pop each controller in order within the loop, then you'll have to wait until the animation completes and the view controller is popped, to pop the next one. – FryAnEgg Jul 05 '17 at 19:14
  • @rmaddy would 'continue' work best for that? – Slavic the Slavic Jul 05 '17 at 19:18
  • No. You only want to call `popToViewController` once. You need `break`. `continue` would be pointless. – rmaddy Jul 05 '17 at 19:19
  • @FryAnEgg what's the best way of doing that. Maybe if you could provide some code. – Slavic the Slavic Jul 05 '17 at 19:20
  • @rmaddy alright. I'll do that. – Slavic the Slavic Jul 05 '17 at 19:21
  • @rmaddy I added the break. Still having the same issue tho. – Slavic the Slavic Jul 05 '17 at 19:26
  • @Slavic the Slavic have you tried to print and check what Is the view controller count you have what are you getting how many times loop execute? – Tushar Sharma Jul 05 '17 at 19:32
  • @TusharSharma I printed what controllers are being pushed and popped. I'll put the code up. Not 100% sure if I'm using it properly tho. – Slavic the Slavic Jul 05 '17 at 19:34
  • @rmaddy is correct in saying that you only need to call popToViewController once. If your navigation stack is A(root), B, C, D, E, you can go through navController.viewControllers until you find the controller you want to pop to (say 'B'), then calling navController.popToViewController(controllerB, animated: true) will pop E and D and C and leave B on top. – FryAnEgg Jul 05 '17 at 19:59
  • @FryAnEgg I added a break as rmaddy said. It didn't seem to make a difference tho. – Slavic the Slavic Jul 05 '17 at 20:05
  • Yes, with or without a break, your code should only pop to the first controller you call popToViewController for. With a break, you'll exit the loop right there, without a break, the subsequent pops will fail because the animation hasn't completed or the next controller has already been popped. – FryAnEgg Jul 05 '17 at 20:16
  • @FryAnEgg Ah alright. So what's the best way of going about this. Not sure if there is a way to animate once for all the popped controllers. – Slavic the Slavic Jul 05 '17 at 20:30

2 Answers2

1

Your confusion may simply be the way you are trying to "check" that the VCs are "popped".

Suppose you have gone:

root->TableView->A->B->A->B->B->B->`

At that point, the only VC that is visible is the last instance of A. So when you call

navController.popToViewController(controller, animated: true)

viewWillDisappear() will only be called on the last instance of A - none of the other VC instances will "disappear" because they are not visible.

If you want to confirm the other VCs in the stack are being "removed", put this in each view controller:

deinit() {
    print("I'm being removed:", self)
}

The other part of the question - do you want to animate through the process? So you would actually see the VCs "walk back up the stack"? If so, follow @FryAnEgg's link to Completion block for popViewController

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Alright, thanks. I will implement your way of checking. – Slavic the Slavic Jul 05 '17 at 21:33
  • So I implemented your check, and found out that there is a print statement for each push made. Meaning that it does pop for every push. So thanks for that! Its still strange the way memory increases tho. I guess the issue might be somewhere else. – Slavic the Slavic Jul 05 '17 at 21:41
  • Are you using Instruments to check memory? Are you seeing any Leaks? Keep in mind, iOS does a lot of memory management "behind the scenes" so to speak... you may just not be looking at the right thing, or you *are*, but iOS is just doing its thing :) – DonMag Jul 05 '17 at 21:45
  • I've used the profiler, to check for leaks, and whether the references are kept or not. It did not help. There is another ios tool I know of but my VM seems to have a hard time running it. – Slavic the Slavic Jul 05 '17 at 21:50
0

Try something like this:

 var theControllerIWantToPopTo = controllerB // or whatever other condition
 if let navController = self.navigationController {
    for controller in navController.viewControllers {
        if controller is TableViewController {
           if controller == theControllerIWantToPopTo {
              navController.popToViewController(controller, animated: true)
              break
           }
        }
     }
 }

Remember, popToViewController will pop all controllers until the chosen one is on top, as opposed to popViewController which will only pop the top controller. If you want to pop them one at a time with animation on each pop see: Completion block for popViewController

FryAnEgg
  • 493
  • 4
  • 12
  • Its giving me an error on the line that says 'if controller == theControllerIWantToPopTo'. The error reads 'Binary operator '==' cannot be applied to operand... – Slavic the Slavic Jul 05 '17 at 20:55
  • In the original code I provided, TableViewController was the view controller that I wanted to pop back to. The var 'theControllerIWantToPopTo' seems like it might be redundant. – Slavic the Slavic Jul 05 '17 at 20:59
  • Is 'TableViewController' an instance or a class? However you do it, find the one controller instance you want to pop to, call popTo, and it will pop everything in between. I think there will only be one animation shown, even though its popping multiple controllers. – FryAnEgg Jul 05 '17 at 21:15
  • TableViewController is a class. It's giving me that error tho. I put var theControllerIWantToPopTo = TableViewController.self – Slavic the Slavic Jul 05 '17 at 21:31
  • So @DonMag showed me a better way to check whether views are popped properly. According to it, they are being popped properly, it wasn't a problem with my stack, just the way I was checking it. Thanks for your help tho. – Slavic the Slavic Jul 05 '17 at 21:43