Passing Data in Swift

2019-01-16 11:54发布

问题:

I have been looking for an answer for this, but have only found answers for segues.

I have viewController1 with a button that segues to viewController2. There is no code for this, I set it up through Interface builder. On viewController2 I have a button that dismisses itself with

 self.dismissViewControllerAnimated(true, completion, nil)

I want to pass a string from viewController2 back to viewController1 when the view is dismissed. How do I go about doing this? Also, I am using swift.

Thanks in advance!

回答1:

There are two common patterns, both of which eliminate the need for viewController2 to know explicitly about viewController1 (which is great for maintainability):

  1. Create a delegate protocol for your for viewController2 and set viewController1 as the delegate. Whenever you want to send data back to viewController1, have viewController2 send the "delegate" the data

  2. Setup a closure as a property that allows passing the data. viewController1 would implement that closure on viewController2 when displaying viewController2. Whenever viewController2 has data to pass back, it would call the closure. I feel that this method is more "swift" like.

Here is some example code for #2:

class ViewController2 : UIViewController {
    var onDataAvailable : ((data: String) -> ())?

    func sendData(data: String) {
        // Whenever you want to send data back to viewController1, check
        // if the closure is implemented and then call it if it is
        self.onDataAvailable?(data: data)
    }
}

class ViewController1 : UIViewController {
   func doSomethingWithData(data: String) {
        // Do something with data
    }
    override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
        // When preparing for the segue, have viewController1 provide a closure for
        // onDataAvailable
        if let viewController = segue.destinationViewController as? ViewController2 {
            viewController.onDataAvailable = {[weak self]
                (data) in
                if let weakSelf = self {
                    weakSelf.doSomethingWithData(data)
                }
            }
        }
    }
}


回答2:

I used the code from the first answer in a transition between controllers WITHOUT prepareForSegue and worked for me as well. Here's the sample code.

The First View Controller:

@IBAction func dpAgendaClick(sender:UIBarButtonItem) {
    ///instantiating view controller with identifier
    if let datePickerViewController = storyboard?.instantiateViewControllerWithIdentifier("DatePickerViewController") 
        as? DatePickerViewController {
            ///bring instantiated view controller to front
            self.presentViewController(datePickerViewController, animated: true, completion: nil)
            ///wrapping the data returned
            datePickerViewController.onDataFiltroAvailable = {[weak self]
                (dataFiltro) in
                if let weakSelf = self {
                    ///use dataFiltro here
                }
}

The second View Controller:

var onDataFiltroAvailable: ((dataFiltro: String) -> ())?
///private var
var dataFiltro: String = ""
///the returning data is obtained on the datePickerChanged event
@IBAction func datePickerChanged(sender: UIDatePicker) {
    let dateFormatter = NSDateFormatter()
    dateFormatter.dateStyle = NSDateFormatterStyle.ShortStyle
    dateFormatter.dateFormat = "yyyy-MM-dd"
    dataFiltro = dateFormatter.stringFromDate(datePicker.date)
}

///dismiss the controller on button click
@IBAction func dpOkClick(sender: UIButton) {
    ///"returning" the data
    self.onDataFiltroAvailable?(dataFiltro: dataFiltro)
    dismissViewControllerAnimated(true, completion: nil) 
}


回答3:

(Swift 2.1, Xcode 7, iOS9) If you don't want it to be tightly coupled only between 2 ViewControllers, You can also use the Notification Design Pattern (Post & Observe), which is mainly used to pass on the same object/information from one VC to multiple View Controllers.

For your scenario : In VC2.swift :

@IBAction func BackBtn(sender: UIButton) {  
  NSNotificationCenter.defaultCenter().postNotificationName("ThisIsTheMessage", object: nil, userInfo:["ObjectBeingSent":yourObject])

}

And in VC1.swift :

override func viewDidLoad() {
        super.viewDidLoad()
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("yourFunction:"), name: "ThisIsTheMessage", object: nil)
    }



func yourFunction(theNotification : NSNotification) {

           if let extractInfo = theNotification.userInfo {
     //code to use the object sent from VC2, by extracting the object details
    }
     }

Common Practise is:

  1. Pass data forward -> Use PrepareForSegue
  2. Pass data backward to the previous View Controller-> Protocol and Delegation
  3. Pass data across multiple View Controllers -> Notifications : Post and Observe(observe in all the View controllers where you are using the object details)