Как пройти prepareForSegue: объект

358

У меня есть много аннотаций в виде карты (с rightCalloutAccessoryкнопками). Кнопка выполнит переход от этого mapviewк tableview. Я хочу передать tableviewдругой объект (который содержит данные) в зависимости от того, какая кнопка вызова была нажата.

Например: (полностью выдуманный)

  • annotation1 (Остин) -> передать данные объекта 1 (относится к Остину)
  • annotation2 (Даллас) -> передать данные объекта 2 (относится к Далласу)
  • annotation3 (Хьюстон) -> передать данные obj 3 и так далее ... (вы поняли)

Я могу определить, какая кнопка выноски была нажата.

Я использую prepareForSegue: для передачи данных obj к месту назначения ViewController. Поскольку я не могу заставить этот вызов принять дополнительный аргумент для нужного мне объекта данных, каковы некоторые изящные способы достижения того же эффекта (динамический объект данных)?

Любой совет будет оценен.

chizzle
источник

Ответы:

674

Просто возьмите ссылку на целевой контроллер представления в prepareForSegue:методе и передайте туда любые нужные вам объекты. Вот пример ...

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    // Make sure your segue name in storyboard is the same as this line
    if ([[segue identifier] isEqualToString:@"YOUR_SEGUE_NAME_HERE"])
    {
        // Get reference to the destination view controller
        YourViewController *vc = [segue destinationViewController];

        // Pass any objects to the view controller here, like...
        [vc setMyObjectHere:object];
    }
}

ПЕРЕСМОТР: Вы также можете использовать performSegueWithIdentifier:sender:метод, чтобы активировать переход к новому виду на основе выбора или нажатия кнопки.

Например, предположим, у меня было два контроллера представления. Первая содержит три кнопки, а вторая должна знать, какая из этих кнопок была нажата перед переходом. Вы можете подключить кнопки до IBActionв вашем коде, который использует performSegueWithIdentifier:метод, как это ...

// When any of my buttons are pressed, push the next view
- (IBAction)buttonPressed:(id)sender
{
    [self performSegueWithIdentifier:@"MySegue" sender:sender];
}

// This will get called too before the view appears
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"MySegue"]) {

        // Get destination view
        SecondView *vc = [segue destinationViewController];

        // Get button tag number (or do whatever you need to do here, based on your object
        NSInteger tagIndex = [(UIButton *)sender tag];

        // Pass the information to your destination view
        [vc setSelectedButton:tagIndex];
    }
}

РЕДАКТИРОВАТЬ: демо-приложение, которое я первоначально приложил, теперь шесть лет, поэтому я удалил его, чтобы избежать путаницы.

Саймон
источник
Спасибо, но я хочу установить [vc setMyObjectHere:object];это динамически. то есть obj1 для button1, obj2 для button2 Проблема в том, что я не могу передать аргумент. Есть ли способ обойти это?
chizzle
2
Я обновил свой пост загружаемым примером того, о чем я говорю.
Саймон
это сработало! Огромное спасибо. В качестве дополнительного примечания prepareForSegue: имеет аргумент UIControl, который является родительским классом UIButton (таким образом, может получить тег): D
chizzle
Подготавливается ли метод prepareForSegue, даже если вы просто нажимаете на контроллер навигации без использования раскадровки?
Закданс
Это один из самых подробных ответов, которые я когда-либо видел. Хорошие примеры кода, решает проблему, предлагает загружаемый образец ... вау. Я впечатлен!
Левигес
82

Иногда полезно избегать создания зависимости во время компиляции между двумя контроллерами представления. Вот как вы можете сделать это, не заботясь о типе контроллера представления назначения:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.destinationViewController respondsToSelector:@selector(setMyData:)]) {
        [segue.destinationViewController performSelector:@selector(setMyData:) 
                                              withObject:myData];
    } 
}

Таким образом, пока ваш конечный контроллер представления объявляет открытое свойство, например:

@property (nonatomic, strong) MyData *myData;

Вы можете установить это свойство в предыдущем контроллере представления, как я описал выше.

Macondo2Seattle
источник
12
Это действительно вопрос мнения. У меня (пока) не было ситуации, когда я не хотел «жестко» контролировать контроллеры представления, хотя я действительно ценю то, что вы говорите, и это может быть важно в правильных обстоятельствах.
Саймон
2
@Simon: да, просто выбери подход, который тебе больше подходит. Например, в приложении, над которым я сейчас работаю, мой подход имеет большой смысл, поскольку я продолжаю добавлять контроллеры представления, которым требуется один и тот же объект данных. Быть способным соединить их с помощью простого перехода и знать, что они получат нужные данные, очень удобно.
Macondo2Seattle
10
Это не вопрос мнения, это просто неправильно :) Фраза «Принятый ответ - не лучший способ сделать это» неверна. Должно быть написано: «В некоторых случаях вам нужно делать это ...»
Толстяк
2
Я предпочитаю метод Саймона. Как он скажет мне ошибки во время компиляции. Например, если я пропустил объявление myData в контроллере представления назначения, я узнаю об этом немедленно. Но для вашего сценария ваш подход кажется хорошим!
Абдуррахман Мубин Али
14
Этот подход абсолютно создает зависимость, так как вы требуете, чтобы контроллер представления назначения имел a setMyData:. Это зависимость. Тот факт, что вы используете селектор, чтобы избежать ошибки компиляции, следует рассматривать как слабость вашего подхода, а не как выгоду. Меня шокирует, как многие разработчики утратили концепцию, согласно которой ошибки времени компиляции следует предпочитать ошибкам времени выполнения.
Nate
21

В Swift 4.2 я бы сделал что-то подобное:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let yourVC = segue.destination as? YourViewController {
        yourVC.yourData = self.someData
    }
}
Реми Силия
источник
Вам нужно позвонить: `super.prepare (для: segue, sender: sender)`?
Андрей К
16

У меня есть класс отправителя , как это

@class MyEntry;

@interface MySenderEntry : NSObject
@property (strong, nonatomic) MyEntry *entry;
@end

@implementation MySenderEntry
@end

Я использую этот класс отправителя для передачи объектовprepareForSeque:sender:

-(void)didSelectItemAtIndexPath:(NSIndexPath*)indexPath
{
    MySenderEntry *sender = [MySenderEntry new];
    sender.entry = [_entries objectAtIndex:indexPath.row];
    [self performSegueWithIdentifier:SEGUE_IDENTIFIER_SHOW_ENTRY sender:sender];
}

-(void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_SHOW_ENTRY]) {
        NSAssert([sender isKindOfClass:[MySenderEntry class]], @"MySenderEntry");
        MySenderEntry *senderEntry = (MySenderEntry*)sender;
        MyEntry *entry = senderEntry.entry;
        NSParameterAssert(entry);

        [segue destinationViewController].delegate = self;
        [segue destinationViewController].entry = entry;
        return;
    }

    if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_HISTORY]) {
        // ...
        return;
    }

    if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_FAVORITE]) {
        // ...
        return;
    }
}
neoneye
источник
мы используем prepareForSegue или мы реализуем это, и предполагается, что prepareForSegue вызывается сам по себе, т.е. нам не нужно делать что-то вроде[self prepareForSegue]
Honey
15

Я столкнулся с этим вопросом, когда пытался научиться передавать данные из одного View Controller в другой. Мне нужно что-то визуальное, чтобы помочь мне учиться, поэтому этот ответ является дополнением к другим уже здесь. Это немного более общий вопрос, чем первоначальный вопрос, но его можно адаптировать к работе.

Этот базовый пример работает так:

введите описание изображения здесь

Идея состоит в том, чтобы передать строку из текстового поля в первом контроллере представления метке во втором контроллере представления.

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

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    // This function is called before the segue
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        // get a reference to the second view controller
        let secondViewController = segue.destinationViewController as! SecondViewController

        // set a variable in the second view controller with the String to pass
        secondViewController.receivedString = textField.text!
    }

}

Контроллер второго вида

import UIKit

class SecondViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    // This variable will hold the data being passed from the First View Controller
    var receivedString = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        // Used the text from the First View Controller to set the label
        label.text = receivedString
    }

}

Запомни

  • Сделайте переход, controlнажав на кнопку и перетащив ее в контроллер второго вида.
  • Подключите розетки для UITextFieldи UILabel.
  • Установите первый и второй View Controllers для соответствующих файлов Swift в IB.

Источник

Как отправить данные через segue (swift) (учебник YouTube)

Смотрите также

View Controllers: передача данных вперед и передача данных назад (более полный ответ)

Suragch
источник
5

Для Swift используйте это,

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    var segueID = segue.identifier

    if(segueID! == "yourSegueName"){

        var yourVC:YourViewController = segue.destinationViewController as YourViewController

        yourVC.objectOnYourVC = setObjectValueHere!

    }
}
Мухаммед Зайд Патан
источник
4

Я реализовал библиотеку с категорией на UIViewController, которая упрощает эту операцию. По сути, вы устанавливаете параметры, которые хотите передать, в NSDictionary, связанном с элементом пользовательского интерфейса, который выполняет переход. Работает и с ручными переходами.

Например, вы можете сделать

[self performSegueWithIdentifier:@"yourIdentifier" parameters:@{@"customParam1":customValue1, @"customValue2":customValue2}];

для ручного перехода или создайте кнопку с переходом и используйте

[button setSegueParameters:@{@"customParam1":customValue1, @"customValue2":customValue2}];

Если контроллер представления назначения не совместим с ключом для кодирования значения ключа, ничего не происходит. Он также работает со значениями ключа (полезно для раскручивания сегментов). Проверьте это здесь https://github.com/stefanomondino/SMQuickSegue

Стефано Мондино
источник
2

Мое решение похоже.

// In destination class: 
var AddressString:String = String()

// In segue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
   if (segue.identifier == "seguetobiddetailpagefromleadbidder")
    {
        let secondViewController = segue.destinationViewController as! BidDetailPage
        secondViewController.AddressString = pr.address as String
    }
}
AG
источник
1

Просто используйте эту функцию.

 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let index = CategorytableView.indexPathForSelectedRow
    let indexNumber = index?.row
    let VC = segue.destination as! DestinationViewController
   VC.value = self.data

}
Парт Баро
источник
0

Я использовал это решение, чтобы я мог сохранить вызов segue и передачу данных в одной и той же функции:

private var segueCompletion : ((UIStoryboardSegue, Any?) -> Void)?

func performSegue(withIdentifier identifier: String, sender: Any?, completion: @escaping (UIStoryboardSegue, Any?) -> Void) {
    self.segueCompletion = completion;
    self.performSegue(withIdentifier: identifier, sender: sender);
    self.segueCompletion = nil
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    self.segueCompletion?(segue, sender)
}

Вариант использования будет что-то вроде:

func showData(id : Int){
    someService.loadSomeData(id: id) {
        data in
        self.performSegue(withIdentifier: "showData", sender: self) {
            storyboard, sender in
            let dataView = storyboard.destination as! DataView
            dataView.data = data
        }
    }
}

Мне кажется, что это работает, но я не уверен на 100%, что функции выполнения и подготовки всегда выполняются в одном потоке.

dannrob
источник