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

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

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

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

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

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

Ответы [ 42 ]

1637 голосов
/ 16 марта 2012

Этот вопрос, кажется, очень популярен здесь на stackoverflow, поэтому я подумал, что постараюсь дать лучший ответ, чтобы помочь людям, начинающим в мире iOS, как я.

Надеюсь, этот ответ достаточно ясенчтобы люди поняли, и что я ничего не пропустил.

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

Передача данных в контроллер представления из другого контроллера представления.Этот метод можно использовать, если вы хотите передать объект / значение из одного контроллера представления в другой контроллер представления, который вы, возможно, помещаете в стек навигации.

Для этого примера у нас будет ViewControllerA иViewControllerB

Чтобы передать значение BOOL из ViewControllerA в ViewControllerB, мы сделаем следующее.

  1. в ViewControllerB.h создадим свойство дляBOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. в ViewControllerA вам нужно рассказать о ViewControllerB, поэтому используйте

    #import "ViewControllerB.h"
    

    Тогда, где вы хотите загрузитьвид напримерdidSelectRowAtIndex или немного IBAction, вам нужно установить свойство в ViewControllerB, прежде чем вы поместите его в стек навигации.

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.isSomethingEnabled = YES;
    [self pushViewController:viewControllerB animated:YES];
    

    Это установит isSomethingEnabled в ViewControllerB в BOOL значениеYES.

Передача данных с использованием сегментов

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

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

Таким образом, чтобы передать BOOL из ViewControllerA в ViewControllerB, мы бывыполните следующие действия:

  1. в ViewControllerB.h создайте свойство для BOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. в ViewControllerA, которое необходиморасскажите о ViewControllerB, поэтому используйте

    #import "ViewControllerB.h"
    
  3. Создайте переход от ViewControllerA до ViewControllerB на раскадровке и дайте ему идентификатор, в этом примере мыЯ назову его "showDetailSegue"

  4. Далее нам нужно добавить метод к ViewControllerA, который вызывается при выполнении любого перехода, из-за этого нам нужно определить, какой вызов был вызвана затем сделай что-нибудьВ нашем примере мы проверим "showDetailSegue", и если это будет выполнено, мы передадим наше значение BOOL в ViewControllerB

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        if([segue.identifier isEqualToString:@"showDetailSegue"]){
            ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
            controller.isSomethingEnabled = YES;
        }
    }
    

    Если ваши представления встроены в контроллер навигации, вам нужно изменитьметод выше слегка к следующему

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        if([segue.identifier isEqualToString:@"showDetailSegue"]){
            UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
            ViewControllerB *controller = (ViewControllerB *)navController.topViewController;
            controller.isSomethingEnabled = YES;
        }
    }
    

    Это установит isSomethingEnabled в ViewControllerB в BOOL значение YES.

PassingВозврат данных

Для передачи данных из ViewControllerB в ViewControllerA необходимо использовать Протоколы и делегаты или Блоки , последние можно использоватькак слабосвязанный механизм для обратных вызовов.

Для этого мы сделаем ViewControllerA делегатом ViewControllerB.Это позволяет ViewControllerB отправлять сообщение обратно на ViewControllerA, что позволяет нам отправлять данные обратно.

Чтобы ViewControllerA был делегатом ViewControllerB, он должен соответствовать протоколу ViewControllerB, которыймы должны уточнить.Это сообщает ViewControllerA, какие методы он должен реализовать.

  1. В ViewControllerB.h, ниже #import, но выше @interface вы указываете протокол.

    @class ViewControllerB;
    
    @protocol ViewControllerBDelegate <NSObject>
    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
    @end
    
  2. далее в ViewControllerB.h вам нужно установить свойство delegate и синтезировать в ViewControllerB.m

    @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
    
  3. В ViewControllerBмы вызываем сообщение на delegate, когда вынимаем контроллер представления.

    NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
    
  4. Вот и все для ViewControllerB.Теперь в ViewControllerA.h, скажите ViewControllerA, чтобы импортировать ViewControllerB и соответствовать его протоколу.

    #import "ViewControllerB.h"
    
    @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
    
  5. В ViewControllerA.m реализуйте следующий метод из нашего протокола

    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
    {
        NSLog(@"This was returned from ViewControllerB %@",item);
    }
    
  6. Прежде чем помещать viewControllerB в стек навигации, нам нужно сообщить ViewControllerB, что ViewControllerA является его делегатом, в противном случае мы получим ошибку.

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.delegate = self
    [[self navigationController] pushViewController:viewControllerB animated:YES];
    

Ссылки

  1. Использование делегирования для связи с другими контроллерами представления в Руководстве по программированию контроллера представления
  2. Шаблон делегата

NSNotification Center Это еще один способ передачи данных.

// add observer in controller(s) where you want to receive data
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil];

-(void) handleDeepLinking:(NSNotification *) notification {
    id someObject = notification.object // some custom object that was passed with notification fire.
}

// post notification
id someObject;
[NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];

Передача данных из одного класса в другой (Классом может быть любой контроллер, менеджер сети / сеанса, подкласс UIView или любой другой класс)

Блоки являются анонимными функциями.

В этом примере данные передаются из Контроллер B в Контроллер A

определить блок

@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h

добавить обработчик блока (слушатель) где вам нужно значение (например, вам нужен ваш ответ API в ControllerA или вам нужны данные ContorllerB в A)

// in ContollerA.m

- (void)viewDidLoad {
    [super viewDidLoad];
    __unsafe_unretained typeof(self) weakSelf = self;
    self.selectedVoucherBlock = ^(NSString *voucher) {
        weakSelf->someLabel.text = voucher;
    };
}

Перейти к контроллеру B

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ControllerB *vc = [storyboard instantiateViewControllerWithIdentifier:@"ControllerB"];
vc.sourceVC = self;
    [self.navigationController pushViewController:vc animated:NO];

пожарный блок

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: 
(NSIndexPath *)indexPath {
    NSString *voucher = vouchersArray[indexPath.row];
    if (sourceVC.selectVoucherBlock) {
        sourceVC.selectVoucherBlock(voucher);
    }
    [self.navigationController popToViewController:sourceVC animated:YES];
}

Другой рабочий пример для блоков

166 голосов
/ 11 августа 2015

Swift

Существует множество объяснений здесь и вокруг StackOverflow, но если вы новичок, просто пытающийся получить что-то базовое для работы, попробуйте посмотреть этот учебник YouTube (это то, что помогло мне, наконец, понятькак это сделать).

Передача данных на следующий View Controller

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

enter image description here

Создать макет раскадровкив Интерфейсном Разработчике.Чтобы сделать переход, вы просто Control нажмите на кнопку и перетащите на контроллер второго вида.

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

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

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    // This function is called before the segue
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        // get a reference to the second view controller
        let secondViewController = segue.destination 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
    }

}

Не забудьте

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

Передача данных обратно на предыдущий контроллер представления

Чтобы передать данные обратно со второго контроллера представления на первый контроллер представления, вы используете протокол иделегировать .Это видео представляет собой очень четкое описание этого процесса:

Ниже приведен пример на основе видео (с некоторыми изменениями).

enter image description here

Создание макета раскадровкив Интерфейсном Разработчике.Опять же, чтобы перейти к следующему этапу, вы просто перетаскиваете Control с кнопки на контроллер второго вида.Установите идентификатор segue для showSecondViewController.Кроме того, не забудьте подключить розетки и действия, используя имена в следующем коде.

Первый контроллер вида

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

import UIKit

class FirstViewController: UIViewController, DataEnteredDelegate {

    @IBOutlet weak var label: UILabel!

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showSecondViewController" {
            let secondViewController = segue.destination as! SecondViewController
            secondViewController.delegate = self
        }
    }

    func userDidEnterInformation(info: String) {
        label.text = info
    }
}

Обратите внимание на использование нашего пользовательского протокола DataEnteredDelegate.

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

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

import UIKit

// 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? = nil

    @IBOutlet weak var textField: UITextField!

    @IBAction func sendTextBackButton(sender: AnyObject) {

        // call this method on whichever class implements our delegate protocol
        delegate?.userDidEnterInformation(info: textField.text!)

        // go back to the previous view controller
        _ = self.navigationController?.popViewController(animated: true)
    }
}

Обратите внимание, что protocol находится вне класса View Controller.

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

130 голосов
/ 06 марта 2011

M в MVC предназначен для «Модели», а в парадигме MVC роль классов моделей заключается в управлении данными программы.Модель является противоположностью представления - представление знает, как отображать данные, но ничего не знает о том, что делать с данными, тогда как модель знает все о том, как работать с данными, но ничего не о том, как их отображать.Модели могут быть сложными, но они не должны быть такими: модель для вашего приложения может быть такой же простой, как массив строк или словарей.

Роль контроллера - посредничество между представлением и моделью.,Следовательно, им нужна ссылка на один или несколько объектов вида и один или несколько объектов модели.Допустим, ваша модель представляет собой массив словарей, каждый из которых представляет одну строку в вашей таблице.Корневое представление для вашего приложения отображает эту таблицу, и оно может отвечать за загрузку массива из файла.Когда пользователь решает добавить новую строку в таблицу, он нажимает какую-то кнопку, и ваш контроллер создает новый (изменяемый) словарь и добавляет его в массив.Чтобы заполнить строку, контроллер создает контроллер подробного представления и дает ему новый словарь.Контроллер подробного представления заполняет словарь и возвращает.Словарь уже является частью модели, поэтому больше ничего не должно происходить.

92 голосов
/ 08 апреля 2014

Существуют различные способы получения данных в другом классе в iOS.Например -

  1. Прямая инициализация после выделения другого класса.
  2. Делегирование - для передачи данных обратно
  3. Уведомление - для передачи данных нескольким классам в одномвремя
  4. Сохранение в NSUserDefaults - для последующего доступа к нему
  5. Классы Singleton
  6. Базы данных и другие механизмы хранения, такие как plist и т. д.

Но для простого сценария передачи значения другому классу, распределение которого выполняется в текущем классе, наиболее распространенным и предпочтительным методом будет прямая установка значений после распределения.Это делается следующим образом: -

Мы можем понять это, используя два контроллера - Controller1 и Controller2

Предположим, что в классе Controller1 вы хотите создать объект Controller2 и нажать егос передачей строкового значения.Это можно сделать следующим образом: -

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj passValue:@"String"];
    [self pushViewController:obj animated:YES];
}

В реализации класса Controller2 будет эта функция, так как -

@interface Controller2  : NSObject

@property (nonatomic , strong) NSString* stringPassed;

@end

@implementation Controller2

@synthesize stringPassed = _stringPassed;

- (void) passValue:(NSString *)value {

    _stringPassed = value; //or self.stringPassed = value
}

@end

Вы также можете напрямую установить свойства Controller2Класс таким же образом, как это:

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj setStringPassed:@"String"];  
    [self pushViewController:obj animated:YES];
}

Для передачи нескольких значений вы можете использовать несколько параметров, таких как: -

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passValue:@“String1” andValues:objArray withDate:date]; 

Или если вам нужно передать более 3 параметров, которыесвязаны с общей функцией, вы можете сохранить значения в классе Model и передать этот modelObject следующему классу

ModelClass *modelObject = [[ModelClass alloc] init]; 
modelObject.property1 = _property1;
modelObject.property2 = _property2;
modelObject.property3 = _property3;

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passmodel: modelObject];

Таким образом, если вы хотите -

1) set the private variables of the second class initialise the values by calling a custom function and passing the values.
2) setProperties do it by directlyInitialising it using the setter method.
3) pass more that 3-4 values related to each other in some manner , then create a model class and set values to its object and pass the object using any of the above process.

Надеюсь, это поможет

82 голосов
/ 14 марта 2011

После дополнительных исследований выяснилось, что протоколы и делегаты - правильный / предпочтительный способ Apple сделать это.

В итоге я использовал этот пример

Обмен данными между контроллерами представления идругие объекты @ iPhone Dev SDK

Работает нормально и позволяет мне передавать строку и массив вперед и назад между моими представлениями.

Спасибо за вашу помощь

63 голосов
/ 14 октября 2013

Я нахожу самую простую и элегантную версию с проходящими блоками.Давайте назовем контроллер представления, который ожидает возвращенные данные, как «A» и возвращающий контроллер представления как «B».В этом примере мы хотим получить 2 значения: первое из Type1 и второе из Type2.

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

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.destinationViewController isKindOfClass:[BViewController class]])
    {
        BViewController *viewController = segue.destinationViewController;

        viewController.callback = ^(Type1 *value1, Type2 *value2) {
            // optionally, close B
            //[self.navigationController popViewControllerAnimated:YES];

            // let's do some action after with returned values
            action1(value1);
            action2(value2);
        };

    }
}

и контроллер представления «B» должен объявить свойство обратного вызова, BViewController.h:

// it is important to use "copy"
@property (copy) void(^callback)(Type1 *value1, Type2 *value2);

Чем в файле реализации BViewController.m после того, как мы получили желаемые значения для возврата нашего обратного вызова, должен быть вызван:

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

53 голосов
/ 06 апреля 2015

Во многих из приведенных ответов содержится некоторая полезная информация, но ни один из них не дает полного ответа на вопрос.

Вопрос касается передачи информации между контроллерами представления. В приведенном конкретном примере запрашивается передача информации между представлениями, но с учетом самооценки новизны iOS первоначальный плакат, скорее всего, подразумевал между viewControllers, а не между представлениями (без какого-либо участия ViewControllers). Кажется, что все ответы сосредоточены на двух контроллерах представления, но что, если приложение развивается, чтобы задействовать более двух контроллеров представления в обмене информацией?

На оригинальном плакате также спрашивалось о синглетах и использовании AppDelegate . На эти вопросы нужно ответить.

Чтобы помочь кому-то еще, смотрящему на этот вопрос, который хочет получить полный ответ, я попытаюсь дать его.

Сценарии применения

Вместо того, чтобы проводить весьма гипотетическое, абстрактное обсуждение, оно помогает иметь в виду конкретные применения. Чтобы помочь определить ситуацию с двумя представлениями контроллера и ситуацией с более чем двумя представлениями, я собираюсь определить два конкретных сценария применения.

Сценарий один: максимум два контроллера представления когда-либо должны обмениваться информацией. Смотрите диаграмму один.

diagram of original problem

В приложении есть два контроллера вида. Существует ViewControllerA (форма ввода данных) и View Controller B (список продуктов). Элементы, выбранные в списке продуктов, должны соответствовать элементам, отображаемым в текстовом поле в форме ввода данных. В этом сценарии ViewControllerA и ViewControllerB должны взаимодействовать напрямую друг с другом, а не с другими контроллерами представления.

Сценарий два : более двух контроллеров представления должны совместно использовать одну и ту же информацию. См. Диаграмму два.

home inventory application diagram

В приложении есть четыре контроллера вида. Это приложение на основе вкладок для управления домашним инвентарем. Три контроллера представления представляют по-разному отфильтрованные представления одних и тех же данных:

  • ViewControllerA - Предметы роскоши
  • ViewControllerB - Незастрахованные товары
  • ViewControllerC - Инвентарь всего дома
  • ViewControllerD - Добавить новый элемент формы

Каждый раз, когда отдельный элемент создается или редактируется, он также должен синхронизироваться с другими контроллерами представления. Например, если мы добавляем лодку в ViewControllerD, но она еще не застрахована, тогда лодка должна появиться, когда пользователь перейдет к ViewControllerA (Предметы роскоши), а также ViewControllerC (Весь инвентарь дома), но не когда пользователь перейдет к ViewControllerB (не застрахованные предметы). Нам нужно заботиться не только о добавлении новых элементов, но и об удалении элементов (которые могут быть разрешены с любого из четырех контроллеров представления) или редактировании существующих элементов (что можно разрешить из «Формы добавления нового элемента», с целью повторного использования того же самого). для редактирования).

Поскольку все контроллеры представления должны совместно использовать одни и те же данные, все четыре контроллера представления должны оставаться в синхронизации, и, следовательно, должна быть какая-то связь со всеми другими контроллерами представления, всякий раз, когда какой-либо один контроллер представления изменяет базовый данные. Должно быть совершенно очевидно, что мы не хотим, чтобы каждый контроллер представления взаимодействовал напрямую друг с другом в этом сценарии. В случае, если это не очевидно, рассмотрим, было ли у нас 20 различных контроллеров представления (а не только 4). Насколько сложно и подвержено ошибкам уведомлять каждый из 19 контроллеров представления каждый раз, когда один контроллер представления вносит изменения?

Решения: делегаты и шаблон наблюдателя и синглтоны

В первом сценарии у нас есть несколько жизнеспособных решений, как и другие ответы

  • перетекает
  • Делегаты
  • установка свойств на контроллерах вида напрямую
  • NSUserDefaults (на самом деле плохой выбор)

Во втором сценарии у нас есть другие жизнеспособные решения:

  • Шаблон наблюдателя
  • Одиночки

A singleton - это экземпляр класса, который является единственным экземпляром, существующим в течение его жизни. Синглтон получил свое название от того факта, что это единственный экземпляр. Обычно разработчики, которые используют синглтоны, имеют специальные методы класса для доступа к ним.

+ (HouseholdInventoryManager*) sharedManager; {
    static dispatch_once_t onceQueue;
    static HouseholdInventoryManager* _sharedInstance;

    // dispatch_once is guaranteed to only be executed once in the
    // lifetime of the application
    dispatch_once(&onceQueue, ^{
        _sharedInstance = [[self alloc] init];
    });
    return _sharedInstance;
}

Теперь, когда мы понимаем, что такое синглтон, давайте обсудим, как синглтон вписывается в схему наблюдателя. Шаблон наблюдателя используется для того, чтобы один объект реагировал на изменения другого объекта. Во втором сценарии у нас есть четыре разных контроллера представления, которые все хотят знать об изменениях в базовых данных. «Базовые данные» должны принадлежать одному экземпляру, одиночке. «Знать об изменениях» достигается путем наблюдения изменений, внесенных в синглтон.

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

#import <Foundation/Foundation.h>

@class JGCHouseholdInventoryItem;

@interface HouseholdInventoryManager : NSObject
/*!
 The global singleton for accessing application data
 */
+ (HouseholdInventoryManager*) sharedManager;


- (NSArray *) entireHouseholdInventory;
- (NSArray *) luxuryItems;
- (NSArray *) nonInsuredItems;

- (void) addHouseholdItemToHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) editHouseholdItemInHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) deleteHoueholdItemFromHomeInventory:(JGCHouseholdInventoryItem*)item;
@end

Когда коллекция предметов домашнего инвентаря изменяется, контроллеры представления должны быть осведомлены об этом изменении. Приведенное выше определение класса не дает понять, как это будет происходить. Нам нужно следовать схеме наблюдателя. Контроллеры представления должны формально соблюдать sharedManager. Есть два способа наблюдать за другим объектом:

  • Наблюдение значения ключа (KVO)
  • NSNotificationCenter.

Во втором сценарии у нас нет ни одного свойства HouseholdInventoryManager, которое можно было бы наблюдать с помощью KVO. Поскольку у нас нет единственного свойства, которое легко наблюдать, шаблон наблюдателя в этом случае должен быть реализован с использованием NSNotificationCenter. Каждый из четырех контроллеров представления будет подписываться на уведомления, а sharedManager будет отправлять уведомления в центр уведомлений, когда это необходимо. Менеджеру инвентаря не нужно ничего знать о контроллерах представления или экземплярах каких-либо других классов, которые могут быть заинтересованы в знании, когда изменяется коллекция предметов инвентаря; NSNotificationCenter заботится об этих деталях реализации. Контроллеры представления просто подписываются на уведомления, а менеджер данных просто публикует уведомления.

Многие начинающие программисты пользуются тем, что в жизни приложения всегда есть ровно один Application Delegate , который доступен во всем мире. Начинающие программисты используют этот факт для помещения объектов и функций в appDelegate для удобства доступа из любой точки приложения. Тот факт, что AppDelegate является синглтоном, не означает, что он должен заменить все остальные синглтоны. Это плохая практика, поскольку она ложится слишком большим бременем на один класс, нарушая хорошие объектно-ориентированные практики. Каждый класс должен иметь четкую роль, которую легко объяснить, часто просто именем класса.

Каждый раз, когда ваш Application Delegate начинает раздуваться, начинайте удалять функциональность в одиночку. Например, основной стек данных не следует оставлять в AppDelegate, а вместо этого следует помещать в его собственный класс, класс coreDataManager.

Ссылки

39 голосов
/ 11 апреля 2014

Передача данных из ViewController 2 (назначение) в viewController 1 (Source) является более интересной вещью.Предполагая, что вы используете storyBoard, это все способы, которые я обнаружил:

  • Делегат
  • Уведомление
  • Пользовательские настройки по умолчанию
  • Singleton

Это уже обсуждалось здесь.

Я обнаружил, что есть и другие способы:

- Использование обратных вызовов блока:

использовать его вprepareForSegue метод в VC1

NextViewController *destinationVC = (NextViewController *) segue.destinationViewController;
[destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC)
{
    self.blockLabel.text = destination.blockTextField.text;
}];

- Использование раскадровок Размотка (выход)

Реализация метода с аргументом UIStoryboardSegue в VC 1, например, так:

-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { }

В storyBoard подключите кнопку «Возврат» к зеленой кнопке «Выход» (Размотка) видеомагнитофона.Теперь у вас есть переход, который «возвращается», поэтому вы можете использовать свойство destinationViewController в prepareForSegue в VC2 и изменить любое свойство VC1, прежде чем оно вернется.

  • Еще один вариант использования раскадровокОтмена (выход) - вы можете использовать метод, который вы написали в VC1

    -(IBAction)UnWindDone:(UIStoryboardSegue *)segue {
        NextViewController *nextViewController = segue.sourceViewController;
        self.unwindLabel.text = nextViewController.unwindPropertyPass;
    } 
    

    И в prepareForSegue из VC1 вы можете изменить любое свойство, которым вы хотите поделиться.

В обоих вариантах раскрутки вы можете установить свойство тега кнопки и проверить его в prepareForSegue.

Надеюсь, я что-то добавил к обсуждению.

:) Приветствия.

39 голосов
/ 22 февраля 2013

В OP не упоминались контроллеры представлений, но так много ответов, что я хотел бы рассказать о том, что некоторые из новых функций LLVM позволяют упростить эту задачу при передаче данных из одного контроллера представлений в другой, а затем получить некоторые результаты обратно.

Сегменты раскадровки, блоки ARC и LLVM делают это проще, чем когда-либо для меня. Некоторые ответы вышеупомянутых раскадровок и сегментов уже были, но все еще полагались на делегирование. Определение делегатов, безусловно, работает, но некоторым людям может быть легче передавать указатели или блоки кода.

С UINavigators и segues, есть простые способы передачи информации на подчиненный контроллер и получения информации обратно. ARC упрощает передачу указателей на объекты, производные от NSObjects, поэтому, если вы хотите, чтобы вспомогательный контроллер добавил / изменил / изменил некоторые данные для вас, передайте ему указатель на изменяемый экземпляр. Блоки облегчают прохождение действий, поэтому, если вы хотите, чтобы подчиненный контроллер вызывал действие на контроллере более высокого уровня, передайте ему блок. Вы определяете блок для принятия любого количества аргументов, которые имеют смысл для вас. Вы также можете спроектировать API для использования нескольких блоков, если это подходит лучше.

Вот два тривиальных примера клея Segue. Первый простой показывает один параметр, переданный для ввода, второй для вывода.

// Prepare the destination view controller by passing it the input we want it to work on
// and the results we will look at when the user has navigated back to this controller's view.

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

     // This parameter gives the next controller the data it works on.
     segueHandoffWithInput:self.dataForNextController

     // This parameter allows the next controller to pass back results
     // by virtue of both controllers having a pointer to the same object.
     andResults:self.resultsFromNextController];
}

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

// Prepare the destination view controller by passing it the input we want it to work on
// and the callback when it has done its work.

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

     // This parameter gives the next controller the data it works on.
     segueHandoffWithInput:self.dataForNextController

     // This parameter allows the next controller to pass back results.
     resultsBlock:^(id results) {
         // This callback could be as involved as you like.
         // It can use Grand Central Dispatch to have work done on another thread for example.
        [self setResultsFromNextController:results];
    }];
}
38 голосов
/ 27 сентября 2013

Существует несколько способов обмена данными.

  1. Вы всегда можете поделиться данными, используя NSUserDefaults.Задайте значение, которым вы хотите поделиться с выбранным вами ключом, и получите значение из NSUserDefault, связанное с этим ключом в следующем контроллере представления.

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
    
  2. Вы можете просто создать свойство в viewcontrollerA.Создайте объект viewcontrollerA в viewcontrollerB и присвойте этому свойству желаемое значение.

  3. Для этого вы также можете создать собственные делегаты.

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