4

I have 4 scenes in my project, and page1 can segue(type is show) to page2, then can segue to page3, then to page4, then back to page1.

You understand that in 1 second from my storyboard:

enter image description here

The class of controller of these four scene is ViewController:

import UIKit

class ViewController: UIViewController {

    static var count: Int = 1

    var id = count

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        ViewController.count += 1
        print("ViewController#\(id) inited.")
    }

    deinit {
        print("ViewController#\(id) deinited.")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        print("ViewController#\(id) loaded.")
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("ViewController#\(id) appeared.")
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        print("ViewController#\(id) disappeared.")
    }

}

Then after I finished the segue circle(page1 > page2 > page3 > page4 > page1), it outputs:

ViewController#1 inited.
ViewController#1 loaded.
ViewController#1 appeared.
ViewController#2 inited.
ViewController#2 loaded.
ViewController#2 appeared.
ViewController#1 disappeared.
ViewController#3 inited.
ViewController#3 loaded.
ViewController#3 appeared.
ViewController#2 disappeared.
ViewController#4 inited.
ViewController#4 loaded.
ViewController#4 appeared.
ViewController#3 disappeared.
ViewController#5 inited.
ViewController#5 loaded.
ViewController#5 appeared.
ViewController#4 disappeared.

That's not what I wanted. I think there are 5 ViewControllers in my heap(because no deinit called). Indeed, we just need 1 ViewController which is to control page1. What should I do to destroy 4 useless ViewController in the heap of my App?

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
Sayakiss
  • 6,878
  • 8
  • 61
  • 107
  • 1
    You can change the codes that segue to page1 to make page1 the root view controller, that'll remove the entire stack of view controllers you have – Ben Ong Dec 07 '16 at 10:02
  • they are still exist in heap because you use "show" for segue, show means i want the controller, keep it for me. if you don't want them you can use 'Present Modally' instead of 'show' ant then dismiss the controller. – Mina Dec 07 '16 at 10:11
  • If I reached the first view controller by performing the segue that connect from page 4 to page 1, should I see the back (pop) button to let me back to the fourth page? – Ahmad F Dec 07 '16 at 10:11
  • @AhmadF No, you shouldn't see the back button. Indeed, there is no back button in my pages. – Sayakiss Dec 07 '16 at 10:56

4 Answers4

2

Pushing/Popping

NOTE:

This answer is ONLY valid if you embed a Navigation Controller, if not (as the this question's case), check my answer about Presenting/Dsimissing.


I think that there are 2 ways to perform the "page4 to page1" segue:

  • Somewhere in your code (I assume that it's in the action of "Back To Page1" button), you are calling performSegue method,

OR

  • You directly ctrl + drag to the first ViewController.

If you followed the the first way, replace the code of performSegue method with popToRootViewController method, as follows:

// "backToPage01Tapped" is just an example, it should be your method's name...
@IBAction func backToPage01Tapped(_ sender: Any) {
    navigationController?.popToRootViewController(animated: true)
}

If you followed the second way, go to storyboard, select the "page4 to page1" segue and delete it! Instead, add an IBAction to the button and let be similar to the code snippet above.

Mainly, for your case, I suggest to popToRootViewController:

Pops all the view controllers on the stack except the root view controller and updates the display.

Seems that's exactly what are your trying to achieve.

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
  • 1
    I followed your advice, but I just find `navigationController` is `nil` in my case. I think the reason for that is I don't embed my view into a navigation controller. – Sayakiss Dec 07 '16 at 11:19
  • Our UI designer give me a design which are just some simple pages like in my example(no back button, no titles), so I don't use `navigationController`. – Sayakiss Dec 07 '16 at 11:21
  • @Sayakiss that's right, it seems that there is no embed navigation controller, so navigation treated as "presenting" not "pushing - show". – Ahmad F Dec 07 '16 at 11:22
  • But the problem is, when `navigationController` is `nil`, then `navigationController?.popToRootViewController(animated: true)` won't take effect. Just let me stay in Page4 and no segue happens. – Sayakiss Dec 07 '16 at 11:23
  • Well, how the showing the next view controller animating looks like? is it from right to left (pushing)? or is it from bottom to top (presenting)? – Ahmad F Dec 07 '16 at 11:24
  • Here is my [screen capture](http://g.recordit.co/6VDgQWgTve.gif), a more [smooth one](http://giphy.com/gifs/3oz8xvlQX6PWoUlhRe) – Sayakiss Dec 07 '16 at 11:35
  • @Sayakiss This is presenting, popToRootViewController is not the case here :). I will add another answer for this case. – Ahmad F Dec 07 '16 at 11:39
  • But I think it's `show` segue, at least the storyboard claims it is. You can check that by my [screen shot](https://i.stack.imgur.com/wgLqa.jpg). – Sayakiss Dec 07 '16 at 11:43
  • In previous version of xcode, adding "show segues" without embed navigation controller causing to crash, now, it by default will be treated as a "show modally" segue. – Ahmad F Dec 07 '16 at 11:46
2

Presenting/Dismissing

NOTE:

If you are working with a navigation controller, you might want to check my answer about Pushing/Popping.


The action of "Back To Page1" in the fourth View Controller-, should be similar to:

@IBAction func backToPage01Tapped(_ sender: Any) {
    presentingViewController?.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
}

Note that the reason of why the number of presentingViewController are three, because it's the number of previous View Controllers for the fourth one.

If you have only two previous View Controllers, then you must chain backwards twice and call dismiss from two View Controllers back:

presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)

and so on...

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
  • Works like a charm! Thanks! – Sayakiss Dec 07 '16 at 11:59
  • @Sayakiss Welcome mate :) Hope it sounds logical to you and glad to help. – Ahmad F Dec 07 '16 at 12:03
  • 1
    Not only did this not work for me, but the window returned to the view controller I just attempted to dismiss and I got a warning that I'm not supposed to dismiss a view controller while it's being presented (not on top). – ScottyBlades Nov 21 '18 at 06:05
1

What you did is simply wrong, don't use forward segue to go back from 4 to 1, you have to use unwind segue, or just set vc 1 in your navigation controller's viewControllers array, if you dont have navigation controller, just call delegate to vc1 to dismiss it's presenting controller, or set rootViewController back to vc1

Tj3n
  • 9,837
  • 2
  • 24
  • 35
  • Sorry, I'm new to iOS development, and I just don't understand how to implement `just call delegate to vc1 to dismiss it's presenting controller`. Could you please explain it in details, or give an example or an article? – Sayakiss Dec 07 '16 at 10:10
  • The easiest way is probably use unwind, delegate is a bit harder to understand, you can read about unwind [here](http://ashishkakkad.com/2015/04/work-with-unwind-segue-in-swift-language-ios-8/) or [here](http://stackoverflow.com/questions/12561735/what-are-unwind-segues-for-and-how-do-you-use-them) – Tj3n Dec 07 '16 at 10:31
0

If you don't intend to do an unwind segue and return to a previous view controller's latest state, and you want to deallocate a prior view controller, then you are essentially treating one of the later view controllers as a new rootViewController, and until you reassign the root view controller property on the AppDelegate that first view controller won't be deallocated due to the rootViewController having a strong reference to it.

UIApplication.shared.keyWindow?.rootViewController = VCTwo
ScottyBlades
  • 12,189
  • 5
  • 77
  • 85