When to present a ViewController programatically

While working on my app, I came across this scenario where I have a ViewController containing a UITableView. The UITableViewCell has a UIImage, tapping which opens another ViewController that presents a list of images to be selected. Phew…!!

To segue or not to segue!

Firstly, I tried adding a segue from the First VC to the Second VC. Calling a method when tapping UITapGestureRecognizer and then calling performSegue method:

performSegue(withIdentifier: "chooseicon", sender: sender)

and then:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) { ...}

But that opens the Second VC even if I tap anywhere on the UITableViewCell.

I wanted the Second VC to be opened only when tapping the image in the cell, so in this scenario, I did not use segue and instead use Present for the new VC inside the Tap gesture method as shown in the Safe Present method:

if let iconVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "IconPickerVC") as? IconPickerVC
{
present(iconVC, animated: true, completion: {
iconVC.delegate = self
})
}

IconPickerVC is the second VC with the identity added in the Main.Storyboard as shown below:

IconPickerVC identity

You need to use Push when you’re using NavigationController. If you do not use NavigationController, simply use the Present way.

If you need to know how I set the image from the Second VC to the UITableViewCell of the First VC, check my post here.

Return selected image to Previous ViewController

A very common scenario in an iOS App is to open another ViewController from one ViewController and return the value of the selected row to the previous ViewController and set the image e.g. setting an icon on the First ViewController.

You can perform the following steps to achieve this scenario:

1. Add a delegate variable on your second view controller. Please name it delegateand not Delegate as per naming convention.

weak var delegate: FirstVC?

2. Add functions to the First ViewController or create a protocol that the FirstVC will adhere to and that you want your second view controller delegate variable to perform.

extension FirstVC {
func selectedImage(_ imageName : UIImage) {
iconName = imgName
//Implement the logic to set image.
}
}

3. While pushing second view from first view controller, set your first view controller as delegate of second view controller. Something like this:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "chooseicon" {
if let iconVC = segue.destination as? SecondVC {
iconVC.delegate = self
}
}
}

4. In your second view controller once image is selected call delegate of second view controller. e.g. use didSelectRowAt of UITableViewController as shown below:

let icons = [ "img1",  "img2",  "img3"]

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.delegate?.setSelectedImage(icons[indexPath.row])
self.dismiss(animated: true, completion: nil)
}

5. Implement logic for selectedImage in your first view controller and use the passed image as per Step 2.