Need to free memory after Modal Segues, but I need both my Segues to pass data from A to B and B to A

穿精又带淫゛_ 提交于 2020-01-07 05:03:09

问题


I am making a game for iOS with SpriteKit.

I have 2 Viewcontrollers. One is the GameViewController and the other one is the MenuViewController. Let's call them A and B respectively.

When the player dies, a function is called in GameScene.swift that launches a modal "Lost" Segue to B. There, the player can restart the game or buy a life and a "Back" Segue is called to A.

I need to dismiss the additional Views that get created each time I call a segue.

Problem is: I need the "Lost" Segue to send data about the Score to View B and I need the "Back" Segue to send data to View A about wether or not the player used a life.

I have implemented all this. But now I need to find how to dismiss old views that keep eating the device's memory, thus leading to lag and crash.

I have googled for hours and hours. No solution was adapted to my situation.

The solutions I found either caused my app to bug, data not to be passed or views not to be generated.

I will not add code here since there is a LOT. But I am sure the answer is actually really easy, just not for a beginner like me.

I think a possible solution would be an unwind segue from B to A ?

But do unwind segues pass data along ?

Moreover, I found no answer I could understand on how to use an unwind segue.

I exhausted all my possibilities. Stack Exchange is my last chance.


回答1:


You definitely should use an unwind segue to return to the previous viewController, otherwise as you have found your memory usage increases until your apps quits.

I created the following example from your description. It uses a standard segue to move from the GameViewController to the MenuViewController and it uses an unwind segue to move from the MenuViewController back to the GameViewController.

The GameViewController has a Player Dies UIButton, a UITextField for entering a score, and a UILabel for displaying the lives.

The MenuViewController has a UILabel for showing the score, a Buy a Life UIButton for adding lives, and a Restart UIButton for returning to the GameViewController.

Here's the code:

GameViewController.swift

import UIKit

class GameViewController: UIViewController {

    @IBOutlet weak var scoreTextField: UITextField!
    @IBOutlet weak var livesLabel: UILabel!

    var lives = 3

    func updateLivesLabel() {
        livesLabel.text = "Lives: \(lives)"
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        updateLivesLabel()
    }

    // This is the function that the unwind segue returns to.
    // You can call it anything you want, but it has to be in
    // the viewController you are returning to, it must be tagged
    // with @IBAction and it must take a UIStoryboardSegue as its
    // only parameter.
    @IBAction func returnFromMenu(segue: UIStoryboardSegue) {
        print("We're back in GameViewController")

        // Update the lives label based upon the value passed in
        // prepareForSegue from the MenuViewController.
        updateLivesLabel()
    }

    @IBAction func goPlayerDies(sender: UIButton) {
        lives--
        self.performSegueWithIdentifier("Lost", sender: self)
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "Lost" {
            let destinationVC = segue.destinationViewController as! MenuViewController
            destinationVC.score = Int(scoreTextField.text ?? "") ?? 0
            destinationVC.lives = lives
        }
    }
}

MenuViewController.swift

import UIKit

class MenuViewController: UIViewController {

    @IBOutlet weak var scoreLabel: UILabel!

    var score = 0
    var lives = 0

    override func viewDidLoad() {
        super.viewDidLoad()

        scoreLabel.text = "Score: \(score)"
    }

    @IBAction func buyLife(sender: UIButton) {
        lives++
    }

    @IBAction func goRestart(sender: UIButton) {
        self.performSegueWithIdentifier("Back", sender: self)
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "Back" {
            let destinationVC = segue.destinationViewController as! GameViewController
            destinationVC.lives = lives
        }
    }
}

This is how you wire up the forward segue to be called programmatically:

Control-drag from ViewController icon to the MenuViewController:


Select Present Modally from the pop-up:


Click on the segue arrow between the viewControllers and give it an identifier in the Attributes Inspector:


This is how you wire up the unwind segue to be called programmatically:

Control-drag from ViewController icon to Exit icon:


Choose returnFromMenu from pop-up:


Click on the Unwind Segue in the Document Outline and give it the identifier "Back" in the Attributes Inspector on the right:


Alternate Answer

Instead of using segues, you can present and dismiss viewControllers manually. The advantage for your app is that the MenuViewController will be allocated only once and will persist for the life of the app. This same viewController will be presented and dismissed repeatedly, but it will not be deallocated which I suspect is leading to your crashes.

The GameViewController will be the initialViewController that is created by the Storyboard. The MenuViewController will be loaded in viewDidLoad of the GameViewController.

To make this work, you need to add an identifier to the MenuViewController so that it can be instantiated by name. Click on the MenuViewController in the Storyboard and set its Storyboard ID in the Identity Inspector:


Here is the code. Note that all mention of segues is gone. Note how viewWillAppear is used to update the viewControllers.

GameViewController.swift

import UIKit

class GameViewController: UIViewController {

    @IBOutlet weak var scoreTextField: UITextField!
    @IBOutlet weak var livesLabel: UILabel!

    var menuViewController: MenuViewController?

    var lives = 3

    func updateLivesLabel() {
        livesLabel.text = "Lives: \(lives)"
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        menuViewController = self.storyboard!.instantiateViewControllerWithIdentifier("MenuViewController") as? MenuViewController
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        updateLivesLabel()
    }

    @IBAction func goPlayerDies(sender: UIButton) {
        lives--
        menuViewController?.score = Int(scoreTextField.text ?? "") ?? 0
        menuViewController?.lives = lives
        self.presentViewController(menuViewController!, animated: true, completion: nil)
    }

}

MenuViewController.swift

import UIKit

class MenuViewController: UIViewController {

    @IBOutlet weak var scoreLabel: UILabel!

    var score = 0
    var lives = 0

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        scoreLabel.text = "Score: \(score)"
    }

    @IBAction func buyLife(sender: UIButton) {
        lives++
    }

    @IBAction func goRestart(sender: UIButton) {
        let destinationVC = self.presentingViewController as! GameViewController
        destinationVC.lives = lives
        self.presentingViewController?.dismissViewControllerAnimated(true, completion: nil)
    }
}


来源:https://stackoverflow.com/questions/33715078/need-to-free-memory-after-modal-segues-but-i-need-both-my-segues-to-pass-data-f

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!