32
votes

I've followed the instructions here but I'm still unsure about this part:

modalVC.delegate=self;
        self.presentViewController(modalVC, animated: true, completion: nil) 

I've tried instantiating the view controller programmatically but still to no avail.

here's my code for when dismissing the modal view controller:

@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
        self.dismiss(animated: true) { 
            //
        }
    }

I'm using storyboard to segue with modal view.

This is the data I wish to transfer back to the parent view controller:

var typeState = "top"
 var categoryState = "casual"

Which are two String values.

Edit:

I've tried to pass data from the modal view controller as shown:

@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
        self.dismiss(animated: true, completion: nil)
        delegate?.sendValue(value: "success")
        if let presenter = presentingViewController as? OOTDListViewController {
            presenter.receivedValue = "test"
        }
    }

whereas on the parent view controller I did as such:

func sendValue(value: NSString) {
        receivedValue = value as String
    }
  @IBAction func printReceivedValue(_ sender: UIButton) {
        print(receivedValue)
    }

I still couldnt receive any value when I hit the print button.

Modal view controller:

protocol ModalViewControllerDelegate
{
    func sendData(typeState: String, categoryState: String)
}

var delegate:ModalViewControllerDelegate!

var typeState = "top"
var categoryState = "casual"
@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
        self.dismiss(animated: true, completion: nil)
        delegate?.sendData(typeState: typeState as String, categoryState: categoryState as String)

    }

Parent view controller:

class parentViewController: UICollectionViewController, ModalViewControllerDelegate {

var typeState: String?
var categoryState: String?
func sendData(typeState: String, categoryState: String) {
        self.typeState = typeState as String
        self.categoryState = categoryState as String
    }
 @IBAction func printReceivedValue(_ sender: UIButton) {
     print(typeState)
 }

Edit:

Here's my new code without using delegate method:

Modal view Controller:

@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
        self.dismiss(animated: true, completion: nil)
        if let presenter = presentingViewController as? OOTDListViewController {
            presenter.typeState = typeState
            presenter.categoryState = categoryState
        }
    }

OOTDListViewController:

@IBAction func presentModalView(_ sender: UIBarButtonItem) {
        let modalView = storyboard?.instantiateViewController(withIdentifier: "filterViewController") as! ModalViewController
        let navModalView: UINavigationController = UINavigationController(rootViewController: modalView)
        self.present(navModalView, animated: true, completion: nil)
    }
@IBAction func printValue(_ sender: UIButton) {
        print(typeState)
        print(categoryState)
    }
4
Are you presenting the viewcontroller with a segue or in code?smeshko
I'm presenting the modal view with storyboard seguelws803
printing still returned nil or a blank answerlws803
You need to set the values before dismissing the viewController, otherwise the presentingViewController will always be nil. So, move the self.dismiss call at the end of the dismissViewController function.smeshko
still doesn't work. Does it have anything to do with me initiating a navigation controller hierarchy?lws803

4 Answers

41
votes

Depending on the data you want to pass, you can create a property in the presenting view controller, which you can set when dismissing the modal view controller, so you can spare yourself the delegate.

For example, you have a ContactsViewController, holding a var contacts: [Contact] = [] property. When you want to create a new contact, you present a modal view controller with the different values you need to create a new Contact object. When you are done and want to dismiss the view controller, you call the function as you did in your code, but set the property in the ContactsViewController. It will look something like this:

@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
    if let presenter = presentingViewController as? ContactsViewController {
        presenter.contacts.append(newContact)
    }
    dismiss(animated: true, completion: nil)
}

EDIT:

If you don't want to use a delegate, this is how you go about it:

In your OOTDListViewController :

var testValue: String = ""

@IBAction func printReceivedValue(_ sender: UIButton) {
    print(testValue)
}

In your modal view controller (I'll call it PresentedViewController) :

@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
    // if your OOTDListViewController is part of a UINavigationController stack, this check will probably fail. 
    // you need to put a breakpoint here and check if the presentingViewController is actually a UINavigationController.
    // in that case, you will need to access the viewControllers variable and find your OOTDListViewController
    if let presenter = presentingViewController as? OOTDListViewController {
        presenter.testValue = "Test"
    }
    dismiss(animated: true, completion: nil)
}

If you want to use a delegate, this is how to do it:

In your OOTDListViewController:

protocol ModalDelegate {
func changeValue(value: String)
}

class OOTDListViewController: ModalDelegate {

var testValue: String = ""
@IBAction func presentViewController() {
    // here, you either create a new instance of the ViewController by initializing it, or you instantiate it using a storyboard. 
    // for simplicity, I'll use the first way
    // in any case, you cannot use a storyboard segue directly, bevause you need access to the reference of the presentedViewController object
    let presentedVC = PresentedViewController() 
    presentedVC.delegate = self
    present(presentedVC, animated: true, completion: nil)
}

func changeValue(value: String) {
     testValue = value
     print(testValue)
}

}

In your PresentedViewController:

class PresentedViewController {
    var delegate: ModalDelegate? 
    var testValue: String = ""

    @IBAction func dismissViewController(_ sender: UIBarButtonItem) {
       if let delegate = self.delegate {
            delegate.changeValue(testValue)
        }
        dismiss(animated: true, completion: nil)
    }

}
5
votes

If using a navigation controller you will have to first grab the UINavigation Controller and then get the correct ViewController from the Navigation Controller stack.

Here's how my code looked in that case.

@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
    if let navController = presentingViewController as? UINavigationController {
       let presenter = navController.topViewController as! OOTDListViewController
        presenter.testValue = "Test"
    }
    dismiss(animated: true, completion: nil)
}
3
votes

i am using the tabbar so the working code as below

            if let tabBar = self.presentingViewController as? UITabBarController {
            let homeNavigationViewController = tabBar.viewControllers![0] as? UINavigationController
            let homeViewController = homeNavigationViewController?.topViewController as! HomeController
            homeViewController._transferedLocationID = self.editingLocationID!
        }
3
votes

You need to call the delegate method in dismissViewController method

@IBAction func dismissViewController(_ sender: UIBarButtonItem) {
        delegate?.sendData(typeState: "top", categoryState: "casual")
        self.dismiss(animated: true) { 
            //
        }
 }

in you Modal ViewController class create delegate

weak var delegate: MyProtocol?

create a protocol with the method name sendData in MyProtocol and in your presentingViewController where you are assigning the delegate, implement the MyProtocol method

protocol MyProtocol: AnyObject {
    func sendData(typeState: String, categoryState: String)
}

class ViewController: UIViewController, MyProtocol {
    var typeState: String?
    var categoryState: String?

    override func viewDidApear() {
        super.viewDidApear()
        presentNewModalVC()
    }
    
    func presentNewModalVC() {
       let modalVC = NewModalViewControllerToBePresented()
       modalVC.delegate = self
       present(modalVC, animated: true)
    }
    
    func sendData(typeState: String, categoryState: String) {
       self.typeState = typeState
       self.categoryState = categoryState
    }
 }