iOS
Передача данных между контроллерами просмотра
Поиск…
Использование Segues (передача данных вперед)
Чтобы передать данные с текущего контроллера представлений на следующий новый контроллер представления (а не на предыдущий контроллер представления) с использованием segues, сначала создайте segue с идентификатором в соответствующей раскадровке. Переопределите метод prepareForSegue
текущего контроллера. Внутри метода проверьте, что вы только что создали его идентификатор. Передайте контроллер представления назначения и передайте ему данные, установив свойства на панели управления снизу.
Установка идентификатора для сегмента:
Сегменты могут выполняться программно или с помощью события действия кнопки, установленного в раскадровке, с помощью Ctrl + перетаскивание до контроллера представления цели. Вы можете при необходимости запросить segue, если необходимо, используя идентификатор segue в контроллере представления:
Objective-C
- (void)showDetail {
[self performSegueWithIdentifier:@"showDetailingSegue" sender:self];
}
стриж
func showDetail() {
self.performSegue(withIdentifier: "showDetailingSegue", sender: self)
}
Вы можете настроить полезную нагрузку prepareForSegue
в переопределенной версии метода prepareForSegue
. Вы можете установить требуемые свойства перед загрузкой контроллера точки назначения.
Objective-C
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:@"showDetailingSegue"]){
DetailViewController *controller = (DetailViewController *)segue.destinationViewController;
controller.isDetailingEnabled = YES;
}
}
стриж
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetailingSegue" {
let controller = segue.destinationViewController as! DetailViewController
controller.isDetailingEnabled = true
}
}
DetailViewController
- это имя второго контроллера представления, а isDetailingEnabled
- общедоступная переменная в этом контроллере представления.
Чтобы расширить этот шаблон, вы можете обработать открытый метод DetailViewController
в качестве псевдоинициализатора, чтобы помочь инициализировать любые требуемые переменные. Это будет переменные документа, которые должны быть установлены на DetailViewController
без необходимости читать через его исходный код. Это также удобное место для установки значений по умолчанию.
Objective-C
- (void)initVC:(BOOL *)isDetailingEnabled {
self.isDetailingEnabled = isDetailingEnabled
}
стриж
func initVC(isDetailingEnabled: Bool) {
self.isDetailingEnabled = isDetailingEnabled
}
Использование шаблона делегата (передача данных назад)
Чтобы передать данные с текущего контроллера представления обратно на предыдущий контроллер просмотра, вы можете использовать шаблон делегата.
В этом примере предполагается, что вы сделали segue в Interface Builder и установили идентификатор showSecondViewController
для showSecondViewController
. Выходы и действия также должны быть подключены к именам в следующем коде.
Контроллер первого взгляда
Код для первого контроллера просмотра
стриж
class FirstViewController: UIViewController, DataEnteredDelegate {
@IBOutlet weak var label: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecondViewController", let secondViewController = segue.destinationViewController as? SecondViewController {
secondViewController.delegate = self
}
}
// required method of our custom DataEnteredDelegate protocol
func userDidEnterInformation(info: String) {
label.text = info
navigationController?.popViewControllerAnimated(true)
}
}
Objective-C
@interface FirstViewController : UIViewController <DataEnteredDelegate>
@property (weak, nonatomic) IBOutlet UILabel *label;
@end
@implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
SecondViewController *secondViewController = segue.destinationViewController;
secondViewController.delegate = self;
}
-(void)userDidEnterInformation:(NSString *)info {
_label.text = info
[self.navigationController popViewControllerAnimated:YES];
}
@end
Обратите внимание на использование нашего пользовательского протокола DataEnteredDelegate
.
Второй контроллер и протокол просмотра
Код для второго контроллера просмотра
стриж
// protocol used for sending data back
protocol DataEnteredDelegate: class {
func userDidEnterInformation(info: String)
}
class SecondViewController: UIViewController {
// making this a weak variable so that it won't create a strong reference cycle
weak var delegate: DataEnteredDelegate?
@IBOutlet weak var textField: UITextField!
@IBAction func sendTextBackButton(sender: AnyObject) {
// call this method on whichever class implements our delegate protocol (the first view controller)
delegate?.userDidEnterInformation(textField.text ?? "")
}
}
Objective-C
@protocol DataEnteredDelegate <NSObject>
-(void)userDidEnterInformation:(NSString *)info;
@end
@interface SecondViewController : UIViewController
@property (nonatomic) id <DataEnteredDelegate> delegate;
@property (weak, nonatomic) IBOutlet UITextField *textField;
@end
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction) sendTextBackButton:(id)sender{
[_delegate userDidEnterInformation:textField.text];
}
@end
Обратите внимание, что protocol
находится вне класса View Controller.
Передача данных назад, используя разматывание
В отличие от segue, который позволяет передавать данные «вперед» с текущего контроллера точки зрения на контроллер точки назначения:
(VC1) -> (VC2)
Используя «разматывать», вы можете сделать обратное, передать данные с адресата или текущего контроллера представления на свой контроллер представления:
(VC1) <- (VC2)
ПРИМЕЧАНИЕ . Обратите внимание, что с помощью функции размотки вы можете сначала передать данные, а затем вывести на экран текущий контроллер (VC2).
Вот как это сделать:
Во-первых, вам нужно будет добавить следующее объявление в представлении контроллера представления (VC1), который является контроллером представления, который мы хотим передать данным:
@IBAction func unwindToPresentingViewController(segue:UIStoryboardSegue)
Важно использовать префикс unwind
, это «сообщает» Xcode, что это метод разматывания, дающий вам возможность использовать его и в раскадровке.
После этого вам нужно будет реализовать метод, он выглядит почти так же, как фактический segue:
@IBAction func unwindToPresentingViewController(segue:UIStoryboardSegue)
{
if segue.identifier == "YourCustomIdentifer"
{
if let VC2 = segue.sourceViewController as? VC2
{
// Your custom code in here to access VC2 class member
}
Теперь у вас есть 2 варианта вызова вызовов для разговора:
- Вы можете «жестко закодировать» вызывать:
self.performSegueWithIdentifier("YourCustomIdentifier", sender: self)
который будет раскручивать вас, когда вы будетеperformSegueWithIdentifier
. - Вы можете связать метод разматывания с помощью
storyboard
с объектом «Выход»: ctrl + перетащить кнопку, которую вы хотите вызвать метод разматывания, к объекту «Выход»:
Отпустите, и у вас будет возможность выбрать свой способ раскрутки:
Передача данных с помощью закрытий (передача данных назад)
Вместо использования шаблона делегирования , который разделяет реализацию в разных UIViewController
класса UIViewController
, вы даже можете использовать closures
для передачи данных назад и вперед. Предполагая, что вы используете UIStoryboardSegue
, в методе prepareForSegue
вы можете легко настроить новый контроллер за один шаг
final class DestinationViewController: UIViewController {
var onCompletion: ((success: Bool) -> ())?
@IBAction func someButtonTapped(sender: AnyObject?) {
onCompletion?(success: true)
}
}
final class MyViewController: UIViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
guard let destinationController = segue.destinationViewController as? DestinationViewController else { return }
destinationController.onCompletion = { success in
// this will be executed when `someButtonTapped(_:)` will be called
print(success)
}
}
}
Это пример использования, и лучше использовать в синтаксисе блока Swift, Objective-C не так просто сделать код более удобочитаемым
Использование закрытия обратного вызова (блока), передающего данные назад
эта тема является классической проблемой в iOS-разработке, и ее решение различно, как показано в другом примере. В этом примере я покажу еще одно ежедневное общее использование: передача данных с использованием closure
путем адаптации примера delegate pattern
на этой странице в closure
обратного вызова!
одна вещь, что этот метод превосходит delegate pattern
вместо того, чтобы разбить код настройки в двух разных местах (посмотрите пример делегата на этой странице, prepareForSegue
, userDidEnterInformation
), а userDidEnterInformation
их вместе (только в prepareForSegue
, я покажу его)
Начать с контроллера второго просмотра
мы должны выяснить, как использовать обратный вызов, тогда мы можем написать его, поэтому мы начинаем с второго контроллера представления, так как там мы используем обратный вызов: когда мы получили новый ввод текста, мы вызываем наш обратный вызов, используя параметр обратного вызова в качестве среды для передачи данных обратно в первый ViewController, обратите внимание, что я сказал, используя параметр обратного вызова, это очень важно, новички (как и я) всегда упускают из виду это и не знают, где начать правильно писать обратный вызов
поэтому в этом случае мы знаем, что наш обратный вызов принимает только один параметр: текст и его тип - String
, давайте объявим его и сделаем это свойство, так как нам нужно заполнить наш первый контроллер представления
Я просто комментирую всю часть delegate
и держу его для сравнения
class SecondViewController: UIViewController {
//weak var delegate: DataEnteredDelegate? = nil
var callback: ((String?)->())?
@IBOutlet weak var textField: UITextField!
@IBAction func sendTextBackButton(sender: AnyObject) {
//delegate?.userDidEnterInformation(textField.text!)
callback?(input.text)
self.navigationController?.popViewControllerAnimated(true)
}
}
Завершить контроллер первого вида
все, что вам нужно сделать, это передать закрытие обратного вызова, и мы закончили, закрытие сделает для нас будущую работу, поскольку мы уже установили его во втором контроллере представления
посмотрите, как сделать наш код короче по сравнению с delegate pattern
//no more DataEnteredDelegate
class FirstViewController: UIViewController {
@IBOutlet weak var label: UILabel!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showSecondViewController" {
let secondViewController = segue.destinationViewController as! SecondViewController
//secondViewController.delegate = self
secondViewController.callback = { text in self.label.text = text }
}
}
// required method of our custom DataEnteredDelegate protocol
//func userDidEnterInformation(info: String) {
// label.text = info
//}
}
и в последнем случае кто-то из вас будет смущен тем, что мы просто передаем данные (закрытие в этом случае) только одним способом: от первого контроллера представления до второго, без прямого возврата со второго контроллера представления, как мы можем считать его инструментом коммуникации? возможно, вам действительно нужно запустить его и доказать это самостоятельно, все, что я скажу, это параметр , это параметр закрытия обратного вызова, который передает данные обратно!
Назначая свойство (передача данных вперед)
Вы можете передавать данные напрямую, назначая свойство следующего контроллера представлений перед тем, как нажать или представить его.
class FirstViewController: UIViewController {
func openSecondViewController() {
// Here we initialize SecondViewController and set the id property to 492
let secondViewController = SecondViewController()
secondViewController.id = 492
// Once it was assign we now push or present the view controller
present(secondViewController, animated: true, completion: nil)
}
}
class SecondViewController: UIViewController {
var id: Int?
override func viewDidLoad() {
super.viewDidLoad()
// Here we unwrapped the id and will get the data from the previous view controller.
if let id = id {
print("Id was set: \(id)")
}
}
}