-3

I'm just starting to learn Swift and I am building a simple application. The main page is simply a table:

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    let theData: [String] = ["BRDA", "HFH", "ENGRII", "HSSB", "GRVT"]
    let cellReuseIdentifier = "cell"

    @IBOutlet weak var tableView: UITableView!
    @IBAction func unwindToHome(segue: UIStoryboardSegue) { }

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.frame = CGRect(x: 0, y: 200, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height-600)
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellReuseIdentifier)

        self.view.addSubview(tableView)
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return theData.count
    }

    internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as UITableViewCell!
        cell.textLabel?.text = theData[indexPath.row]
        return cell
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if (indexPath.row == 0) {
            // segue to BRDA
            performSegue(withIdentifier: "toBRDA", sender: nil)
        } else {
            print("Will add later! You clicked on \(theData[indexPath.row]).")
        }
    }
}

I made the first entry clickable and I got it to segue to another controller. This new controller has an UIImage object and it simply changes colours when you tap on it.

class BRDAController: UIViewController {

    @IBOutlet weak var thePic: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        var tapGesture = UITapGestureRecognizer()
        tapGesture = UITapGestureRecognizer(target: self, action: #selector(BRDAController.didTap(_:)))
        tapGesture.numberOfTapsRequired = 1
        tapGesture.numberOfTouchesRequired = 1
        thePic.addGestureRecognizer(tapGesture)
        thePic.isUserInteractionEnabled = true
        thePic.backgroundColor = UIColor.yellow
        // Do any additional setup after loading the view.
    }

    @objc func didTap(_ sender: UITapGestureRecognizer) {
        // User tapped at the point above. Do something with that if you want.
        if thePic.backgroundColor == UIColor.yellow {
            thePic.backgroundColor = UIColor.green
        } else {
            thePic.backgroundColor = UIColor.yellow
        }
    }

    @IBAction func backButton(_ sender: Any) {
        self.performSegue(withIdentifier: "goBackHome", sender: nil)
    }
}

I used an unwinding segue to go back to my home but when I go back to the other controller, the state has been reset (the colour has been set to its default yellow). How do I go about getting my code to 'remember' its previous state?

Ayumu Kasugano
  • 440
  • 4
  • 13

1 Answers1

4

It depends on the use case. If you want to save the data for...

  • ...after quitting and relaunching the app, save it to user defaults
  • ...the session only, when going back to the previous VC, use the delegate approach

Beyond the session: UserDefaults

@IBAction func backButton(_ sender: Any) {
    UserDefaults.standard.set(thePic.backgroundColor, forKey: "color")
}

...using the UserDefaults extension from here.

Next time you launch your app, read the color like this:

let color = UserDefaults.standard.color(forKey: "color")

Session-only: Delegate approach

Create a delegate protocol:

protocol BRDAControllerDelegate: class {
    func brdaController(_ brdaController: BRDAController, willDismissPassing data: [String: Any])
}

Then, create a delegate property:

class BRDAController: UIViewController {
    weak var delegate: BRDAControllerDelegate?

Just before dismissing, notify the delegate:

    @IBAction func backButton(_ sender: Any) {
        delegate?.brdaController(self, willDismissPassing: ["color": thePic.backgroundColor])
        // dismiss here
    }
}

Inside the presenting view controller, inherit from the delegate method:

class ViewController: ..., BRDAControllerDelegate {
    func brdaController(_ brdaController: BRDAController, willDismissPassing data: [String: Any]) {
        if let color = data["color"] as? UIColor {
            // do something with your color here
        }
    }

and set the delegate to self before transitioning:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    super.prepare(for: segue, sender: sender)
    if let brdaVC = segue.destination as? BRDAController {
        brdaVC.delegate = self
    }
}

Also, I suggest you to dismiss your view controller, instead of creating a new segue back:

@IBAction func backButton(_ sender: Any) {
    // embedded in navigation controller? use:
    navigationController?.popViewController(animated: true)
    // OR, no navigation controller? use:
    dismiss(animated: true)
}
LinusGeffarth
  • 27,197
  • 29
  • 120
  • 174
  • However it is duplicate question but for your hard work to explain things and code Plus one :) – Prashant Tukadiya Mar 06 '19 at 07:42
  • 1
    Appreciate it! :) – LinusGeffarth Mar 06 '19 at 07:43
  • Thanks for the detailed answer! I'm still confused on what exactly I'm supposed to put in the `brdaController` func. Also, do you have recommended resources on dealing with delegates? – Ayumu Kasugano Mar 06 '19 at 08:03
  • You mean what to pass in the dict when passing the color to the delegate? – LinusGeffarth Mar 06 '19 at 08:04
  • Yes, I guess I'm failing to see how this exactly passes data to the `BRDAController`. – Ayumu Kasugano Mar 06 '19 at 08:06
  • Okay. You see, when you do `delegate = self` inside `ViewController`, you tell the `BRDAController` that self (`ViewController` in this case) wants to be notified upon changes. `weak var delegate: BRDAControllerDelegate` effectively *becomes* the `ViewController`. Since you then implement the callback method `willDismissPassing` inside `ViewController`, you can call this method from inside `BRDAController`. – LinusGeffarth Mar 06 '19 at 08:08
  • This also only works because `ViewController` isn't yet dismissed, but still open behind `BRDAController`. Otherwise the delegate would be nil. – LinusGeffarth Mar 06 '19 at 08:09
  • Ah I see! Thanks so much! – Ayumu Kasugano Mar 06 '19 at 08:16