Передача данных между контроллерами представления - PullRequest
1304 голосов
/ 06 марта 2011

Я новичок в iOS и Objective-C и во всей парадигме MVC, и я застрял в следующем:

У меня есть представление, которое действует как форма ввода данных, и я хочу дать пользователю возможность выбрать несколько продуктов. Продукты перечислены в другом представлении с UITableViewController, и я включил несколько вариантов выбора.

У меня вопрос, как мне перенести данные из одного представления в другое? Я буду удерживать выборки на UITableView в массиве, но как мне затем передать это обратно в предыдущее представление формы ввода данных, чтобы его можно было сохранить вместе с другими данными в Core Data при отправке формы?

Я просмотрел и увидел, как некоторые люди объявляют массив в делегате приложения. Я читаю что-то о Singletons, но не понимаю, что это такое, и я читаю что-то о создании модели данных.

Каков будет правильный способ выполнения этого и как я буду это делать?

Ответы [ 42 ]

4 голосов
/ 19 мая 2019

Swift 5

Ну Ответ Мэтта Прайса отлично подходит для передачи данных, но я Я собираюсь переписать его, в последней версии Swift, потому что я считаю, что новый программисты считают, что это бросает вызов из-за нового синтаксиса и методы / рамки, как оригинальный пост в Objective-C.

Существует несколько вариантов передачи данных между контроллерами представления.

  1. Использование навигационного контроллера Push
  2. Использование Segue
  3. Использование делегата
  4. Использование Notification Observer
  5. Использование блока

Я собираюсь переписать его логику в Swift с последней iOS Framework


Передача данных через контроллер навигации Нажмите : Из ViewControllerA в ViewControllerB

Шаг 1. Объявление переменной в ViewControllerB

var isSomethingEnabled = false

Шаг 2. Печать переменной в методе ViewControllerB 'ViewDidLoad

override func viewDidLoad() {
        super.viewDidLoad()
        //Print value received through segue, navigation push
        print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
    }

Шаг 3. В ViewControllerA Передача данных при проталкивании через Navigation Controller

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
        viewControllerB.isSomethingEnabled = true
        if let navigator = navigationController {
            navigator.pushViewController(viewControllerB, animated: true)
        }
    }

Итак, вот полный код для:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //MARK:Passing Data through Navigation PushViewController
    @IBAction func goToViewControllerB(_ sender: Any) {

        if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.isSomethingEnabled = true
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    //MARK:  - Variable for Passing Data through Navigation push   
    var isSomethingEnabled = false

    override func viewDidLoad() {
        super.viewDidLoad()
        //Print value received through navigation push
        print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
    }
}

Передача данных через Segue : Из ViewControllerA в ViewControllerB

Шаг 1. Создайте Segue из ViewControllerA в ViewControllerB и передайте Identifier = showDetailSegue в раскадровке, как показано ниже

enter image description here

Шаг 2. В ViewControllerB Объявите жизнеспособное имя с именем isSomethingEnabled и напечатайте его значение.

Шаг 3. В ViewControllerA передать значение isSomethingEnabled при передаче Segue

Итак, вот полный код:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //MARK:  - - Passing Data through Segue  - - 
    @IBAction func goToViewControllerBUsingSegue(_ sender: Any) {
        performSegue(withIdentifier: "showDetailSegue", sender: nil)
    }

    //Segue Delegate Method
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if (segue.identifier == "showDetailSegue") {
            let controller = segue.destination as? ViewControllerB
            controller?.isSomethingEnabled = true//passing data
        }
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {
    var isSomethingEnabled = false

    override func viewDidLoad() {
        super.viewDidLoad()
        //Print value received through segue
        print("Value of 'isSomethingEnabled' from ViewControllerA : ", isSomethingEnabled)
    }
}

Передача данных через делегата : Из ViewControllerB в ViewControllerA

Шаг 1. Объявление протокола ViewControllerBDelegate в файле ViewControllerB, но вне класса

protocol ViewControllerBDelegate: NSObjectProtocol {

    // Classes that adopt this protocol MUST define
    // this method -- and hopefully do something in
    // that definition.
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

Шаг 2. Объявление экземпляра переменной Delegate в ViewControllerB

var delegate: ViewControllerBDelegate?

Шаг 3. Отправка данных для делегата внутри метода viewDidLoad ViewControllerB

delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")

Шаг 4. Подтверждение ViewControllerBДелегат в ViewControllerA

class ViewControllerA: UIViewController, ViewControllerBDelegate  {
// to do
}

Шаг 5. Подтвердите, что вы реализуете делегат в ViewControllerA

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.delegate = self//confirming delegate
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }

Шаг 6. Реализация метода делегата для получения данных в ViewControllerA

func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
        print("Value from ViewControllerB's Delegate", item!)
    }

Итак, вот полный код для:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController, ViewControllerBDelegate  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //Delegate method
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
        print("Value from ViewControllerB's Delegate", item!)
    }

    @IBAction func goToViewControllerForDelegate(_ sender: Any) {

        if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.delegate = self
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }
    }
}

ViewControllerB

import UIKit

//Protocol decleare
protocol ViewControllerBDelegate: NSObjectProtocol {
    // Classes that adopt this protocol MUST define
    // this method -- and hopefully do something in
    // that definition.
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

class ViewControllerB: UIViewController {
    var delegate: ViewControllerBDelegate?

    override func viewDidLoad() {
        super.viewDidLoad()
        //MARK:  - - - -  Set Data for Passing Data through Delegate  - - - - - -
        delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")
    }
}

Передача данных через Notification Observer : Из ViewControllerB в ViewControllerA

Шаг 1. Установка и публикация данных в наблюдателе уведомлений в ViewControllerB

let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)

Шаг 2. Добавить наблюдателя уведомлений в ViewControllerA

NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)

Шаг 3. Получите значение данных уведомления в ViewControllerA

@objc func methodOfReceivedNotification(notification: Notification) {
        print("Value of notification : ", notification.object ?? "")
    }

Итак, вот полный код для:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController{

    override func viewDidLoad() {
        super.viewDidLoad()

        // add observer in controller(s) where you want to receive data
        NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
    }

    //MARK: Method for receiving Data through Post Notification 
    @objc func methodOfReceivedNotification(notification: Notification) {
        print("Value of notification : ", notification.object ?? "")
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        //MARK:Set data for Passing Data through Post Notification
        let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
    }
}

Передача данных через блок : Из ViewControllerB в ViewControllerA

Шаг 1. Объявление блока в ViewControllerB

var authorizationCompletionBlock: ((Bool) -> ())? = {_ in}

Шаг 2. Установите данные в блоке в ViewControllerB

if authorizationCompletionBlock != nil
        {
            authorizationCompletionBlock!(true)
        }

Шаг 3. Получение данных блока в ViewControllerA

//Receiver Block
                controller!.authorizationCompletionBlock = { isGranted in
                    print("Data received from Block is :", isGranted)
                }

Итак, вот полный код для:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //MARK:Method for receiving Data through Block
        override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
            if (segue.identifier == "showDetailSegue") {
                let controller = segue.destination as? ViewControllerB
                controller?.isSomethingEnabled = true

                //Receiver Block
                controller!.authorizationCompletionBlock = { isGranted in
                    print("Data received from Block is :", isGranted)
                }
            }
        }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    //MARK:Variable for Passing Data through Block
    var authorizationCompletionBlock:((Bool)->())? = {_ in}

    override func viewDidLoad() {
        super.viewDidLoad()

        //MARK:Set data for Passing Data through Block
        if authorizationCompletionBlock != nil
        {
            authorizationCompletionBlock!(true)
        }
    }
}

Вы можете найти полный образец приложения на моем GitHub Пожалуйста, дайте мне знать, если у вас есть какие-либо вопросы по этому вопросу.

3 голосов
/ 13 ноября 2018

С ИСПОЛЬЗОВАНИЕМ УВЕДОМЛЕНИЯ

Для Swift 3

let imageDataDict:[String: UIImage] = ["image": image]

  // post a notification
  NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: imageDataDict) 
  // `default` is now a property, not a method call

 // Register to receive notification in your class
 NotificationCenter.default.addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)

 // handle notification
 func showSpinningWheel(_ notification: NSNotification) {
        print(notification.userInfo ?? "")
        if let dict = notification.userInfo as NSDictionary? {
            if let id = dict["image"] as? UIImage{
                // do something with your image
            }
        }
 }

Для Swift 4

let imageDataDict:[String: UIImage] = ["image": image]

  // post a notification
  NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: imageDataDict) 
  // `default` is now a property, not a method call

 // Register to receive notification in your class
 NotificationCenter.default.addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)

 // handle notification
 @objc func showSpinningWheel(_ notification: NSNotification) {
        print(notification.userInfo ?? "")
        if let dict = notification.userInfo as NSDictionary? {
            if let id = dict["image"] as? UIImage{
                // do something with your image
            }
        }
 }
3 голосов
/ 28 сентября 2015

Это действительно отличный учебник для тех, кто хочет.Вот пример кода:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"myIdentifer]) {
        NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
        myViewController *destViewController = segue.destinationViewController;
        destViewController.name = [object objectAtIndex:indexPath.row];
    }
}
2 голосов
/ 07 мая 2018

у нас есть несколько способов работы с системой делегатов или с использованием storyboardSegue

1- As working with setter and getter method like in viewController.h
   @property (retain, nonatomic) NSString *str;
   now, in viewController.m
   @synthesize str;


   here i have pdf url and segue to another viewController like this and pdfObject is my pdfModel basicilly is NSOBJECT class.  

   str =[NSString stringWithFormat:@"%@",pdfObject.objPath];
NSLog(@"pdfUrl :***: %@ :***:",pdfUrl);

[self performSegueWithIdentifier:@"programPDFViewController_segue" sender:self];

отметка прагмы - навигация

  // In a storyboard-based application, you will often want to do a little preparation before navigation

 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

    if ([[segue identifier] isEqualToString:@"programPDFViewController_segue"]){
    programPDFViewController *pdfVC = [segue destinationViewController];
    [pdfVC setRecievedPdfUrl:str];

   }
 }

Теперь я успешно получил строку URL-адреса в формате pdf и другой ViewControllerи использовать эту строку в веб-представлении ...

2- При работе с такими делегатами у меня есть один класс утилит NSObject, содержащий мои методы dateFormatter, sharedInstance, EscapeWhiteSpaceCharacters, convertImageToGrayScale и другие методы, с которыми я работал на протяжении всего процесса.Приложение теперь в utilities.h

. В этом случае вам не нужно создавать переменные при постоянном анализе данных от одного к другому контроллеру представления один раз, когда ваша созданная строковая переменная в utilities.h просто делает его равным nil;и используется снова

  @interface Utilities : NSObject

  Utilities.h
 +(Utilities*)sharedInstance;

 @property(nonatomic,retain)NSString* strUrl;

теперь в утилитах.m

   @implementation utilities


  +(utilities*)sharedInstance
  {
  static utilities* sharedObj = nil;
  if (sharedObj == nil) {
    sharedObj = [[utilities alloc] init];
    }
   return sharedObj;
  }

now its done come to your firstViewController.m and call delegate

NSString*str =[NSString stringWithFormat:@"%@",pdfObject.objPath];

[Connection sharedInstance].strUrl=nil;
[Connection sharedInstance].strUrl=str;

 Now go to you secondViewController.m directly use it without creating variable 

 in viewwillapear what i did

 -(void)viewWillAppear:(BOOL)animated{
     [super viewWillAppear:YES];

   [self webViewMethod:[Connection sharedInstance].strUrl];

 }


 -(void)WebViewMethod:(NSString)Url{

 // working with webview enjoy coding :D

 }

эта работа делегата надежна с управлением памятью

2 голосов
/ 28 ноября 2015

для отправки данных из одного VC в другой используйте этот простой подход:

YourNextVC *nxtScr = (YourNextVC*)[self.storyboard  instantiateViewControllerWithIdentifier:@"YourNextVC"];//Set this identifier from your storyboard

nxtScr.comingFrom = @"PreviousScreen"l
[self.navigationController nxtScr animated:YES];
2 голосов
/ 12 апреля 2018

Вы можете создать push-переход от исходного viewcontroller к конечному viewcontroller и дать имя идентификатора, как показано ниже enter image description here

Вы должны выполнить переход от didselectRow. Вот так.

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    performSegue(withIdentifier: "segue", sender: self)
}

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

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let index = CategorytableView.indexPathForSelectedRow
    let indexNumber = index?.row
    print(indexNumber!)
    let VC = segue.destination as! AddTransactionVC
   VC.val = CategoryData[indexNumber!] . //You can pass here entire array instead of array element.

}

И вы должны проверить значение в viewdidload целевого viewcontroller и затем сохранить его в базе данных.

override func viewDidLoad{
 if val != ""{
        btnSelectCategory.setTitle(val, for: .normal)
    }
}
1 голос
/ 06 февраля 2019

Я предпочитаю делать это без делегатов и segues.Это можно сделать с помощью пользовательского init или установив дополнительные значения.

1.Пользовательский init

class ViewControllerA: UIViewController {
  func openViewControllerB() {
    let viewController = ViewControllerB(string: "Blabla", completionClosure: { success in
      print(success)
    })
    navigationController?.pushViewController(animated: true)
  }
}

class ViewControllerB: UIViewController {
  private let completionClosure: ((Bool) -> Void)
  init(string: String, completionClosure: ((Bool) -> Void)) {
    self.completionClosure = completionClosure
    super.init(nibName: nil, bundle: nil)
    title = string
  }

  func finishWork() {
    completionClosure()
  }
}

2.Дополнительные переменные

class ViewControllerA: UIViewController {
  func openViewControllerB() {
    let viewController = ViewControllerB()
    viewController.string = "Blabla"
    viewController.completionClosure = { success in
      print(success)
    }
    navigationController?.pushViewController(animated: true)
  }
}

class ViewControllerB: UIViewController {
  var string: String? {
    didSet {
      title = string
    }
  }
  var completionClosure: ((Bool) -> Void)?

  func finishWork() {
    completionClosure?()
  }
}
1 голос
/ 26 марта 2018

При создании приложений для iOS вы всегда должны следовать концепции MVC. Существует два сценария, в которых вы можете передавать данные из ViewController в другой:

  1. Когда в иерархии есть ViewContoller типа «A», и вы хотите отправить некоторые данные в «B», который является следующим viewcontroller. В этом случае вы должны использовать Segue. Просто установите идентификатор для перехода, а затем в «A» VC напишите следующий код:

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "A to B segue identifier" {
            let bViewController = segue.destination as! UIDocumentBrowserViewController
            bViewController.data = someData
        }
    }
    
  2. Когда есть A, который открыл B на себя как модальный (или встраивать). Теперь B viewcontroller должен игнорировать своего родителя. Поэтому лучший способ отправить данные обратно на A - это использовать Delegation. Создайте протокол делегирования в B viewcontroller и свойстве delegate. Так что B сообщит (отправит данные обратно) своему делегату. В A viewcontroller мы реализуем протокол делегирования B viewcontroller и установим self в качестве свойства delegate B viewcontroller в методе prepare(forSegue:).

Вот как это должно быть правильно реализовано. Надеюсь, это поможет

1 голос
/ 09 мая 2017

Я рекомендую блоки / замыкания и пользовательские конструкторы.

Предположим, вам нужно передать строку из FirstViewController в SecondViewController.

Ваш первый контроллер просмотра.

class FirstViewController : UIViewController {

    func moveToViewControllerB() {

        let second_screen = SecondViewController.screen(string: "DATA TO PASS", call_back: {
            [weak self] (updated_data) in
            ///This closure will be called by second view controller when it updates something
        })
        self.navigationController?.pushViewController(second_screen, animated: true)
    }


}

Ваш второй контроллер View

class SecondViewController : UIViewController {

    var incoming_string : String?
    var call_back : ((String) -> Void)?

    class func screen(string: String?, call_back : ((String) -> Void)?) -> SecondViewController {

        let me = SecondViewController(nibName: String(describing: self), bundle: Bundle.main);
        me.incoming_string = string
        me.call_back = call_back
        return me
    }

    // Suppose its called when you have to update FirstViewController with new data.
    func updatedSomething() {

        //Executing block that is implemented/assigned by the FirstViewController.
        self.call_back?("UPDATED DATA")
    }

}
1 голос
/ 22 декабря 2015

Apple может сделать это, используя Segues. Вам нужно использовать функцию prepareForSegue ()

Вокруг много хороших уроков, вот один: https://www.iphonelife.com/content/unleash-your-inner-app-developer-part-21-passing-data-between-controllers

Также ознакомьтесь с документацией Apple по использованию сегментов: https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/UsingSegues.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...