1

At the Moment I have to ViewControllers. You can switch between ViewControllerA and ViewControllerB with show-segues. The problem is that every time I switch back, the ViewControllers state gets reseted. How can I save the setUp of a ViewController?

From A to B

    @IBAction func editButtonTapped(_ sender: Any) {
    let imageCollectionView = self.storyboard?.instantiateViewController(withIdentifier: "ImageCollectionVC") as! ImageCollectionViewController

    self.navigationController?.pushViewController(imageCollectionView, animated: true)
}

From B to A

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    tappedImage = images[indexPath.row]
    performSegue(withIdentifier: "backToPopUpView", sender: self)

}
Chris
  • 1,828
  • 6
  • 40
  • 108
  • "The problem is that every time I switch back, the ViewControllers state gets reseted." Then you are not actually switching back. Can you show some code to let us know what you are currently doing? – Sweeper Nov 08 '19 at 18:03
  • I edited my question – Chris Nov 08 '19 at 18:06
  • Looking at your code you aren't moving back and forwards between view controllers, but creating a new instance of a view controller each time you change. So you are actually doing A > B > A1 > B1 > A2 > B2 etc. which is why state isn't being preserved. You need to Pop view controller B to return to A. Incidentally you are also not using a segue in A to B. – flanker Nov 08 '19 at 18:24
  • Possible duplicate of [Passing Data between View Controllers](https://stackoverflow.com/questions/5210535/passing-data-between-view-controllers) – Yoel Jimenez del valle Nov 08 '19 at 19:10
  • @flanker so how would I do that? – Chris Nov 09 '19 at 16:22
  • @Chris - see my answer below where I cover this in detail. – flanker Nov 09 '19 at 19:24

2 Answers2

1

To move between view controllers passing data both ways you need a few things

  • a consistent method of navigation: segues or pushing/popping via a navigation controller or modally presenting/dismissing, but not a mix of them for one A - B - A transition

  • protocols/deleagtes to allow data to be passed back from th child to the parent.

In the example below navigation is via the navigation controller, and an image is used as an example of how to pass data back to the parent. It should be trivial to adapt this for other circumstances.

The child class needs an agreed interface with its parent to allow it to communicate. This is done with a protocol. In this example we provide a means for the child to pass its updated image back to the parent:

protocol ClassBDelegate {
    func childVCDidComplete(with image: UIImage?)
}

Then create a delegate variable in the child class that can point to any class that adopts the protocol (which in this case will be its parent) and then use that delegate and the protocol's function to pass the image data back. Once the data has been passed back via the delegate, view controller B calls the navigation controller's popViewCntroller method to close itself and return focus to view controller A

class B: UIViewController {
    var delegate: ClassBDelegate?

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        tappedImage = images[indexPath.row]
        delegate?.childVCDidComplete(with: tappedImage)
        navigationController?.popViewController(animated: true)
    }
}

For this all to work, the child view controller's delegate needs to be set to point its parent view controller (A), but before this can happen that class needs to conform to the protocol:

extension A: ClassBDelegate {}
    func childVCDidComplete( with image: UIImage?) {
        self.image = image
    }
}

Now, when instantiating the child view controller the parents set itself as the delegate, thus completing the communication loop.

Class A: UIViewController {
    var image: UIImage?

    @IBAction func editButtonTapped(_ sender: Any) {
        let imageCollectionView = self.storyboard?.instantiateViewController(withIdentifier: "ImageCollectionVC") as! ImageCollectionViewController
        imageCollectionView.delegate = self
        self.navigationController?.pushViewController(imageCollectionView, animated: true)
    }
}
flanker
  • 3,840
  • 1
  • 12
  • 20
  • sorry but I'm struggling to implement this into my code.. Where exactly do I have to implement `extension` and `protocol`? – Chris Nov 11 '19 at 17:56
  • I've used `A` and `B` as shorthand for your A and B view controllers, so replace them with the respective class names. Then you implement the protocol either in its own file or in the same file as View controller B, between the import statements and your class definition. You put the extension to view controller A underneath it's class definition - after the last `}`. It's too wide a scope to explain everything about protocols/delegates here, but if you look on YouTube Sean Allen has a very good video tutorial. – flanker Nov 11 '19 at 18:56
  • I implemented it but now if I click tap on an item nothing happens – Chris Nov 11 '19 at 20:19
  • `popViewController` doesn't do anything even though it gets called, I tested it with a `print` – Chris Nov 11 '19 at 20:24
  • i suspect you haven't quite implemented it correctly, or there is other code you've not shown conflicting with it, as I copied the code from a working mock up. What item where does nothing? popviewcontroller will only work if you've initially presented the view controller using push (with a navigation controller), not if you've presented modally or used a segue. – flanker Nov 11 '19 at 20:43
  • I think I did that. I copied your version of `editButtonTapped` – Chris Nov 11 '19 at 20:46
  • so, I still had a `segue` in my `Storyboard`, which I just deleted but now the `editButtonTapped` doesn't bring me to the other Viewcontroller – Chris Nov 11 '19 at 20:49
  • You need to ensure the edit button is linked to its IBAction and that the code in it is accurately entered (using your correct class names and identifier strings) and runs, and that none of the optional chaining returns nil. – flanker Nov 11 '19 at 21:02
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/202168/discussion-between-chris-and-flanker). – Chris Nov 11 '19 at 21:02
  • Sorry Chris, I've tried my best to help, but I really don't have the time to spend chatting about this. Transitioning between view controllers is the very basics of Swift development and not something that should even need discussing on SO. If you are struggling there are many good tutorials around covering these sorts of topics. – flanker Nov 12 '19 at 00:41
  • the problem I had with the transition was actually a deeper problem but I got that working now. But I implemented your code and its not working for me :( – Chris Nov 12 '19 at 13:04
  • If you've moved things on, maybe post your updated code as a new post with a more focussed question? – flanker Nov 12 '19 at 13:37
0

You are created new ViewControllerA, and you have the following result A -> B -> A. But it is not true, you should dismiss ViewControllerB...

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    tappedImage = images[indexPath.row]
    self.dismiss(animated: true, completion: nil)
    //performSegue(withIdentifier: "backToPopUpView", sender: self)

}
KNK
  • 59
  • 7
  • but I am passing an `UIImage` with that segue. And if I just `self.dismiss` that image doesn't get passed – Chris Nov 08 '19 at 18:16
  • You must pass information by 2 way. 1.With Protocol 2. In ViewControllerB you must have "parentVC: ViewControllerA" or "vcA: ViewControllerA" property... And in ViewControllerA you must write this line imageCollectionView.vcA = self. And in ViewControllerB you can pass self.vcA.image = tappedImage – KNK Nov 08 '19 at 18:20
  • could you elaborate on that please ?:) I can not write `imageCollectionView.vcA = self` in ViewControllerA – Chris Nov 11 '19 at 18:01