Поиск…


Использование Segues (передача данных вперед)

Чтобы передать данные с текущего контроллера представлений на следующий новый контроллер представления (а не на предыдущий контроллер представления) с использованием segues, сначала создайте segue с идентификатором в соответствующей раскадровке. Переопределите метод prepareForSegue текущего контроллера. Внутри метода проверьте, что вы только что создали его идентификатор. Передайте контроллер представления назначения и передайте ему данные, установив свойства на панели управления снизу.

Установка идентификатора для сегмента:

Инспектор атрибутов для segue

Сегменты могут выполняться программно или с помощью события действия кнопки, установленного в раскадровке, с помощью 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 варианта вызова вызовов для разговора:

  1. Вы можете «жестко закодировать» вызывать: self.performSegueWithIdentifier("YourCustomIdentifier", sender: self) который будет раскручивать вас, когда вы будете performSegueWithIdentifier .
  2. Вы можете связать метод разматывания с помощью 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)")
        }
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow