Передача данных в Swift

Я искал ответ на это, но нашел ответы только на переходы.

У меня есть viewController1 с кнопкой, которая переходит в viewController2. Кода для этого нет, я настроил через конструктор интерфейсов. На viewController2 у меня есть кнопка, которая закрывается с помощью

 self.dismissViewControllerAnimated(true, completion, nil)

Я хочу передать строку из viewController2 обратно в viewController1 при закрытии представления. Как мне это сделать? Кроме того, я использую Swift.

Заранее спасибо!


person user3724253    schedule 20.06.2014    source источник


Ответы (3)


Существует два распространенных шаблона, оба из которых устраняют необходимость в том, чтобы viewController2 явно знал о viewController1 (что отлично подходит для удобства сопровождения):

  1. Создайте протокол делегата для вашего viewController2 и установите viewController1 в качестве делегата. Всякий раз, когда вы хотите отправить данные обратно в viewController1, попросите viewController2 отправить «делегату» данные

  2. Настройте замыкание как свойство, которое позволяет передавать данные. viewController1 реализует это закрытие для viewController2 при отображении viewController2. Всякий раз, когда у viewController2 есть данные для возврата, он вызывает замыкание. Я чувствую, что этот метод более «быстрый».

Вот пример кода для #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)
                }
            }
        }
    }
}
person drewag    schedule 20.06.2014
comment
Это замечательно! Что-то вроде легкого внутреннего протокола. Вы можете использовать необязательную цепочку для вызова замыкания без if let... : self.onDataAvailable?(data: data) - person Nate Cook; 20.06.2014
comment
@NateCook спасибо, хороший призыв к необязательной цепочке. Я обновил свой ответ этим. - person drewag; 20.06.2014
comment
Является ли onDataAvailable закрытием. Если да, то как мне сохранить в нем свою строку. Извините, я очень новичок в программировании. - person user3724253; 20.06.2014
comment
@user3724253 user3724253 Да, это переменная-член ViewController2, которая является замыканием. Вы не храните в нем свою строку, вы вызываете ее (как функцию) со своей строкой, когда хотите, чтобы ViewController1 получил данные. Я привел вам пример этого с методом sendData в ViewController2 - person drewag; 20.06.2014
comment
Большое спасибо!! Это сделало это! Буквально спасает жизнь! - person user3724253; 20.06.2014
comment
@user3724253 user3724253 Вы можете получить доступ к себе в замыкании, но из-за проблемы с управлением памятью, вы должны использовать его как слабую ссылку, что немного усложняет ситуацию. Я обновил свой пример, чтобы показать вам. - person drewag; 20.06.2014
comment
@drewag, согласно книге Swift, вы должны использовать weak, когда ссылка может стать нулевой, и unowned, если она не станет нулевой. Если вы используете tabbarcontroller, а viewcontroller1 является одним из начальных контроллеров представления вкладок, будет ли это unowned? (Другими словами, становятся ли начальные контроллеры представления в приложениях на основе вкладок нулевыми?) - person dewyze; 05.01.2015
comment
@dewyze, будет ли onDataAvailable вызываться в ViewController2 после уничтожения ViewController1, зависит от того, как написан остальной код. Здесь недостаточно кода/информации, чтобы сказать наверняка, особенно потому, что мы не знаем, как создаются эти контроллеры представления и хранятся ли они где-либо еще. Мы также не знаем, когда вызывается sendData. Слабые ссылки всегда безопаснее, поэтому использование по умолчанию слабых ссылок всегда является хорошей идеей. - person drewag; 05.01.2015
comment
@drewag Есть ли шанс, что вы обновите пример, чтобы использовать несколько переменных разных типов? - person Jed Grant; 26.05.2015
comment
отличный ответ, я проголосовал за него со своей стороны - person Nischal Hada; 03.03.2016

Я использовал код из первого ответа при переходе между контроллерами БЕЗ подготовкиForSegue и у меня тоже сработал. Вот пример кода.

Контроллер первого представления:

@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
                }
}

Второй контроллер представления:

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) 
}
person Pablo    schedule 21.01.2016

(Swift 2.1, Xcode 7, iOS9). Если вы не хотите, чтобы он был тесно связан только между двумя ViewController, вы также можете использовать Шаблон проектирования уведомлений (Post & Observe), который в основном используется для передачи одного и того же объекта/информации из одного VC в несколько контроллеров представления.

Для вашего сценария: В VC2.swift:

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

}

И в 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
    }
     }

Общая практика:

  1. Передать данные вперед -> Использовать PrepareForSegue
  2. Передать данные назад к предыдущему протоколу View Controller-> и делегированию
  3. Передача данных между несколькими контроллерами представления -> Уведомления: отправка и наблюдение (наблюдайте за всеми контроллерами представления, где вы используете сведения об объекте)
person Naishta    schedule 14.01.2016
comment
вопрос: если кто-то захочет установить строку, равную возвращаемой информации о пользователе в yourFunction(), как это сделать? - person John Harding II; 17.04.2016
comment
извините, я не понял вопроса, не могли бы вы добавить больше деталей, пожалуйста - person Naishta; 18.04.2016