Я новичок в iOS и Objective-C и всей парадигме MVC, и я застрял в следующем:
У меня есть представление, которое действует как форма ввода данных, и я хочу дать пользователю возможность выбрать несколько продуктов. Продукты перечислены в другом представлении с помощью UITableViewController
и я включил несколько вариантов.
Мой вопрос: как мне переносить данные из одного представления в другое? Я буду держать выбор в UITableView
в массиве, но как передать это обратно в предыдущее представление формы ввода данных, чтобы его можно было сохранить вместе с другими данными в Core Data при отправке формы?
Я просмотрел и увидел, как некоторые люди объявляют массив в делегате приложения. Я читал кое-что о Синглтонах, но не понимаю, что это такое, и я кое-что прочитал о создании модели данных.
Каким будет правильный способ выполнения этого и как я буду заниматься этим?
Этот вопрос, кажется, очень популярен здесь, в stackoverflow, поэтому я решил попробовать и дать лучший ответ, чтобы помочь людям, начинающим в мире iOS, как я.
Надеюсь, этот ответ достаточно ясен, чтобы люди поняли и что я ничего не пропустил.
Передача данных вперед
Передача данных вперед в контроллер вида с другого контроллера. Вы должны использовать этот метод, если хотите передать объект/значение с одного контроллера вида на другой контроллер представлений, который вы можете нажать в стек навигации.
В этом примере мы будем иметь ViewControllerA
и ViewControllerB
Чтобы передать значение BOOL
из ViewControllerA
в ViewControllerB
мы сделали бы следующее.
в ViewControllerB.h
создайте свойство для BOOL
@property (nonatomic, assign) BOOL isSomethingEnabled;
в 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
.
Передача данных вперед с помощью Segues
Если вы используете раскадровки, вы, скорее всего, используете segues и вам понадобится эта процедура для передачи данных вперед. Это похоже на приведенное выше, но вместо передачи данных перед тем, как вы нажимаете контроллер вида, вы используете метод, называемый
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
Итак, чтобы передать BOOL
из ViewControllerA
в ViewControllerB
мы сделали бы следующее:
в ViewControllerB.h
создайте свойство для BOOL
@property (nonatomic, assign) BOOL isSomethingEnabled;
в ViewControllerA
вам нужно рассказать об этом ViewControllerB
поэтому используйте
#import "ViewControllerB.h"
Создайте ViewControllerA
из ViewControllerA
в ViewControllerB
на раскадровке и дайте ему идентификатор, в этом примере мы будем называть его "showDetailSegue"
Затем нам нужно добавить метод в ViewControllerA
который вызывается, когда выполняется какой-либо segue, из-за этого нам нужно определить, какой вызов был вызван, а затем что-то сделать. В нашем примере мы проверим "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
.
Передача данных назад
Чтобы передать данные из ViewControllerB
в ViewControllerA
вам необходимо использовать протоколы и делегаты или блоки, последний может использоваться как слабосвязанный механизм для обратных вызовов.
Для этого мы сделаем ViewControllerA
делегатом ViewControllerB
. Это позволяет ViewControllerB
отправлять сообщение обратно в ViewControllerA
позволяя нам отправлять данные обратно.
Чтобы ViewControllerA
был делегатом ViewControllerB
он должен соответствовать протоколу ViewControllerB
, который мы должны указать. Это говорит ViewControllerA
какие методы он должен реализовать.
В ViewControllerB.h
, ниже #import
, но выше @interface
вы указываете протокол.
@class ViewControllerB;
@protocol ViewControllerBDelegate <NSObject>
- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
@end
следующий еще в ViewControllerB.h
вам нужно настроить свойство delegate
и синтезировать в ViewControllerB.m
@property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
В ViewControllerB
мы называем сообщение о delegate
, когда мы выскочить контроллер представления.
NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
[self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
Это для ViewControllerB
. Теперь в ViewControllerA.h
скажите ViewControllerA
импортировать ViewControllerB
и соответствовать его протоколу.
#import "ViewControllerB.h"
@interface ViewControllerA : UIViewController <ViewControllerBDelegate>
В ViewControllerA.m
реализуем следующий метод из нашего протокола
- (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
{
NSLog(@"This was returned from ViewControllerB %@",item);
}
Прежде чем нажать viewControllerB
на стек навигации, нам нужно указать ViewControllerB
что ViewControllerA
является его делегатом, иначе мы получим ошибку.
ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
viewControllerB.delegate = self
[[self navigationController] pushViewController:viewControllerB animated:YES];
Центр NSNotification Это другой способ передачи данных.
// 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];
}
@class ViewControllerB;
выше определения @protocol? Без этого я получаю ошибку «Ожидаемый тип» на ViewControllerB в строке: - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
в объявлении @protocol
Здесь и вокруг StackOverflow есть тонны и тонны объяснений, но если вы новичок, просто пытающийся найти что-то основное для работы, попробуйте посмотреть этот учебник YouTube (это помогло мне, наконец, понять, как это сделать).
Ниже приведен пример, основанный на видео. Идея состоит в том, чтобы передать строку из текстового поля в First View Controller на метку в Second View Controller.
Создайте раскладку в построителе интерфейса. Чтобы сделать segue, вы просто 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
.Чтобы передать данные с второго контроллера представления на первый контроллер представления, вы используете протокол и делегат. Это видео является очень четкой прогулкой, хотя из этого процесса:
Ниже приведен пример, основанный на видео (с несколькими изменениями).
Создайте раскладку в построителе интерфейса. Опять же, чтобы сделать segue, вы просто 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
.
Второй контроллер и протокол просмотра
Код для второго контроллера представления
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.
Что это. Запустив приложение, вы сможете отправить данные с второго контроллера представления на первый.
M в MVC предназначен для "Модели", а в парадигме MVC роль классов моделей заключается в управлении программными данными. Модель противоположна представлению - представление знает, как отображать данные, но ничего не знает о том, что делать с данными, тогда как модель знает все о том, как работать с данными, но ничего о том, как ее отображать. Модели могут быть сложными, но они не обязательно должны быть - модель для вашего приложения может быть такой же простой, как массив строк или словарей.
Роль контроллера заключается в посредничестве между представлением и моделью. Поэтому им нужна ссылка на один или несколько объектов вида и один или несколько объектов модели. Скажем, что ваша модель представляет собой массив словарей, причем каждый словарь представляет одну строку в вашей таблице. Корневой вид для вашего приложения отображает эту таблицу, и он может отвечать за загрузку массива из файла. Когда пользователь решает добавить новую строку в таблицу, они нажимают какую-то кнопку, и ваш контроллер создает новый (изменяемый) словарь и добавляет его в массив. Чтобы заполнить строку, контроллер создает контроллер подробного представления и дает ему новый словарь. Контроллер подробного представления заполняет словарь и возвращает его. Словарь уже является частью модели, поэтому ничего не должно произойти.
Существуют различные способы, с помощью которых данные могут быть получены в другом классе в iOS. Например -
NSUserDefaults
- для доступа к нему позжеНо для простого сценария передачи значения другому классу, распределение которого выполняется в текущем классе, наиболее распространенным и предпочтительным методом будет прямая настройка значений после выделения. Это делается следующим образом: -
Мы можем понять это с помощью двух контроллеров - Controller1 и Controller2
Предположим, что в классе Controller1 вы хотите создать объект Controller2 и нажать его с переданным значением String. Это можно сделать следующим образом: -
- (void)pushToController2 {
Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passValue:@"String"];
[self pushViewController:obj animated:YES];
}
В реализации класса Controller2 будет существовать эта функция as-
@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];
Или, если вам нужно передать более трех параметров, которые связаны с общей функцией, вы можете сохранить значения в классе модели и передать этот объект 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.
Надеюсь, что это поможет
После дополнительных исследований казалось, что протоколы и делегаты являются правильными/предпочтительными для Apple способами.
В итоге я использовал этот пример
Обмен данными между диспетчерами и другими объектами @iPhone Dev SDK
Работал нормально и разрешил мне передавать строку и массив вперед и назад между моими представлениями.
Спасибо за вашу помощь
Я нахожу простейшую и самую элегантную версию с пропущенными блоками. Пусть контроллер имен, который ожидает возвращенных данных как "A", и возвращает контроллер представления как "B". В этом примере мы хотим получить 2 значения: сначала из Type1 и второго из Type2.
Предполагая, что мы используем Storyboard, первый контроллер устанавливает блок обратного вызова, например, во время подготовки segue:
- (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 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 после того, как мы получим желаемые значения для возврата нашего обратного вызова, следует вызвать:
if (self.callback)
self.callback(value1, value2);
Одна вещь, которую нужно помнить, заключается в том, что использование блока часто требует управления сильными и __вековыми ссылками, например объясненными здесь
Во многих ответах есть какая-то хорошая информация, но никто не затрагивает вопрос полностью.
Вопрос задает вопрос о передаче информации между диспетчерами. В приведенном конкретном примере задается вопрос о передаче информации между представлениями, но с учетом самоопределенной новизны для iOS исходный плакат, вероятно, подразумевал между viewControllers, а не между представлениями (без участия в ViewControllers). Кажется, что все ответы сосредоточены на двух контроллерах представлений, но что, если приложение развивается, чтобы задействовать более двух контроллеров представления в обмене информацией?
Оригинальный плакат также спросил о Singletons и использовании AppDelegate. На эти вопросы нужно ответить.
Чтобы помочь кому-либо еще взглянуть на этот вопрос, кто хочет получить полный ответ, я попытаюсь его предоставить.
Сценарии приложений
Вместо того, чтобы иметь весьма гипотетическое, абстрактное обсуждение, оно помогает иметь конкретные приложения. Чтобы определить ситуацию с двумя диспетчерскими контроллерами и ситуацию с контроллером более чем двух точек зрения, я собираюсь определить два конкретных сценария приложения.
Сценарий один: максимум два диспетчера представлений требуют обмена информацией. См. Диаграмму 1.
В приложении есть два контроллера вида. Существует ViewControllerA (Форма ввода данных) и View Controller B (Список продуктов). Элементы, выбранные в списке продуктов, должны соответствовать элементам, отображаемым в текстовом поле в форме ввода данных. В этом случае ViewControllerA и ViewControllerB должны связываться напрямую друг с другом и никакими другими контроллерами представлений.
Сценарий два: более двух диспетчеров представлений должны использовать одну и ту же информацию. См. Диаграмму 2.
В приложении есть четыре контроллера вида. Это приложение на основе вкладок для управления домашним инвентарем. Три диспетчера представлений представляют разные отфильтрованные представления тех же данных:
Каждый раз, когда отдельный элемент создается или редактируется, он также должен синхронизироваться с другими контроллерами представлений. Например, если мы добавим лодку в ViewControllerD, но она еще не застрахована, тогда лодка должна появиться, когда пользователь перейдет в ViewControllerA (Luxury Items), а также ViewControllerC (Entire Home Inventory), но не тогда, когда пользователь переходит к ViewControllerB (незастрахованные элементы). Нам нужно не только добавлять новые элементы, но и удалять элементы (которые могут быть разрешены с любого из четырех контроллеров представления) или редактировать существующие элементы (которые могут быть разрешены из "Добавить новую форму элемента", для редактирования).
Поскольку все контроллеры представлений требуют совместного использования одних и тех же данных, все четыре контроллера представлений должны оставаться в синхронизации, и поэтому для всех других контроллеров представлений должна быть какая-то связь, когда какой-либо один контроллер просмотра изменяет базовые данные. Должно быть достаточно очевидно, что мы не хотим, чтобы каждый контроллер представления напрямую связывался с каждым другим контроллером представления в этом сценарии. В случае, если это не очевидно, подумайте, было ли у нас 20 разных контроллеров представлений (а не только 4). Насколько сложно и подвержено ошибкам было бы уведомлять каждый из других 19 контроллеров представления в любое время, когда один контроллер просмотра внес изменения?
Решения: делегаты и шаблон наблюдателя, а также синглтоны
В первом случае у нас есть несколько жизнеспособных решений, как дали другие ответы
Во втором сценарии у нас есть другие жизнеспособные решения:
Синглтон - это экземпляр класса, этот экземпляр является единственным экземпляром, существовавшим в течение его жизни. Одноэлемент получает свое имя от того, что это единственный экземпляр. Обычно разработчики, которые используют синглтоны, имеют специальные методы класса для доступа к ним.
+ (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. Существует два способа наблюдения за другим объектом:
Во втором сценарии у нас нет единственного свойства объекта HousInventoryManager, которое можно наблюдать с помощью KVO. Поскольку у нас нет единого свойства, которое легко можно наблюдать, шаблон наблюдателя в этом случае должен быть реализован с использованием NSNotificationCenter. Каждый из четырех контроллеров представлений будет подписываться на уведомления, а sharedManager будет отправлять уведомления в центр уведомлений, когда это необходимо. Менеджеру инвентаря не нужно ничего знать о контроллерах представления или экземплярах любых других классов, которые могут быть заинтересованы в том, чтобы знать, когда изменяется коллекция элементов инвентаря; NSNotificationCenter позаботится об этих деталях реализации. Контроллеры просмотра просто подписываются на уведомления, а диспетчер данных просто отправляет уведомления.
Многие новички-программисты используют тот факт, что всегда есть один делегат приложения в течение всего срока действия приложения, доступного во всем мире. Начинающие программисты используют этот факт для заполнения объектов и функциональности в appDelegate в качестве удобства для доступа из любой точки приложения. Просто потому, что AppDelegate является синглом, это не значит, что он должен заменить все остальные синглтоны. Это плохая практика, поскольку она накладывает слишком много нагрузки на один класс, нарушая хорошие объектно-ориентированные практики. Каждый класс должен иметь четкую роль, которую легко объяснить, часто просто по имени класса.
Каждый раз, когда ваш делегат приложений начинает раздуваться, начните удалять функциональность в одиночные игры. Например, основной стек данных не должен оставаться в AppDelegate, но вместо этого должен быть помещен в его собственный класс, класс coreDataManager.
Рекомендации
Передача данных из ViewController 2 (destination) в viewController 1 (Source) является более интересной. Предполагая, что вы используете storyBoard, это все, что я узнал:
Те были обсуждены здесь уже.
Я обнаружил, что есть больше способов:
-Использование обратных вызовов блока:
использовать его в методе prepareForSegue
в VC1
NextViewController *destinationVC = (NextViewController *) segue.destinationViewController;
[destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC)
{
self.blockLabel.text = destination.blockTextField.text;
}];
-Использование раскадровки Unwind (Выход)
Внедрите метод с аргументом UIStoryboardSegue в VC 1, например:
-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { }
В окне storyBoard нажмите кнопку "вернуться" на зеленый "Выход" (Unwind) vc. Теперь у вас есть segue, который "возвращается", поэтому вы можете использовать свойство destinationViewController в файле readyForSegue VC2 и измените любое свойство VC1 до его возврата.
Другой вариант использования раскадровки Undwind (Exit) - вы можете используйте метод, который вы написали в VC1
-(IBAction)UnWindDone:(UIStoryboardSegue *)segue {
NextViewController *nextViewController = segue.sourceViewController;
self.unwindLabel.text = nextViewController.unwindPropertyPass;
}
И в readyForSegue VC1 вы можете изменить любое свойство, которое хотите разделить.
В обоих вариантах размотки вы можете установить свойство тега кнопки и проверить его в подготовитьForSegue.
Надеюсь, я добавил кое-что к обсуждению.
:) Приветствия.
Существует несколько способов совместного использования данных.
Вы всегда можете обмениваться данными с помощью NSUserDefaults
. Установите значение, которое вы хотите разделить, по отношению к выбранному вами ключу и получите значение от NSUserDefault
, связанное с этим ключом в следующем контроллере представления.
[[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
[[NSUserDefaults standardUserDefaults] objectForKey:key]
Вы можете просто создать свойство в viewcontrollerA
. Создайте объект viewcontrollerA
в viewcontrollerB
и назначьте требуемое значение этому свойству.
Вы также можете создавать пользовательские делегаты для этого.
В OP не упоминались контроллеры представлений, но так много ответов, что я хотел подслушивать то, что некоторые из новых функций LLVM позволяют сделать это проще, когда вы хотите передавать данные с одного контроллера вида на другой, а затем получить некоторые результаты.
Сегменты раскадровки, блоки ARC и LLVM делают это легче, чем когда-либо для меня. Некоторые ответы выше упомянутых раскадровки и segue уже, но все еще опирались на делегацию. Определение делегатов, безусловно, работает, но некоторым людям легче найти указатели или блоки кода.
С помощью 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 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];
}];
}
Если вы хотите передать данные с одного контроллера на другой, попробуйте этот код
FirstViewController.h
@property (nonatomic, retain) NSString *str;
SecondViewController.h
@property (nonatomic, retain) NSString *str1;
FirstViewController.m
- (void)viewDidLoad
{
// message for the second SecondViewController
self.str = @"text message";
[super viewDidLoad];
}
-(IBAction)ButtonClicked
{
SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
secondViewController.str1 = str;
[self.navigationController pushViewController:secondViewController animated:YES];
}
Я долго искал это решение, Atlast нашел его. Прежде всего объявите все объекты в файле SecondViewController.h, например
@interface SecondViewController: UIviewController
{
NSMutableArray *myAray;
CustomObject *object;
}
Теперь в вашем файле реализации выделите память для этих объектов, таких как
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Custom initialization
myAray=[[NSMutableArray alloc] init];
object=[[CustomObject alloc] init];
}
return self;
}
Теперь вы выделили память для Array
и объекта. теперь вы можете заполнить эту память, прежде чем нажимать эту ViewController
Перейдите в свой SecondViewController.h и напишите два метода
-(void)setMyArray:(NSArray *)_myArray;
-(void)setMyObject:(CustomObject *)_myObject;
в файле реализации вы можете реализовать функцию
-(void)setMyArray:(NSArray *)_myArray
{
[myArra addObjectsFromArray:_myArray];
}
-(void)setMyObject:(CustomObject *)_myObject
{
[object setCustomObject:_myObject];
}
ожидая, что ваш CustomObject
должен иметь с ним функцию setter.
теперь выполняется ваша основная работа. перейдите в то место, где вы хотите нажать SecondViewController
, и сделайте следующее:
SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]] ;
[secondView setMyArray:ArrayToPass];
[secondView setMyObject:objectToPass];
[self.navigationController pushViewController:secondView animated:YES ];
Соблюдайте орфографические ошибки.
Это не способ сделать это, вы должны использовать делегаты, я предполагаю, что у нас есть два диспетчера представлений ViewController1 и ViewController2, и эта вещь проверки находится в первом и когда ее состояние изменяется, вы хотите что-то сделать в ViewController2, чтобы достичь этого правильно, вы должны сделать следующее:
Добавьте новый файл в проект (Objective-C Protocol) File → New, теперь назовите его ViewController1Delegate или что хотите и напишите между директивами @interface и @end
@optional
- (void)checkStateDidChange:(BOOL)checked;
Теперь перейдите в ViewController2.h и добавьте
#import "ViewController1Delegate.h"
затем измените его определение на
@interface ViewController2: UIViewController<ViewController1Delegate>
Теперь перейдите в ViewController2.m и внутри реализации добавьте:
- (void)checkStateDidChange:(BOOL)checked {
if (checked) {
// Do whatever you want here
NSLog(@"Checked");
}
else {
// Also do whatever you want here
NSLog(@"Not checked");
}
}
Теперь перейдите в ViewController1.h и добавьте следующее свойство:
@property (weak, nonatomic) id<ViewController1Delegate> delegate;
Теперь, если вы создаете ViewController1 внутри ViewController2 после некоторого события, вы должны сделать это так, используя файлы NIB:
ViewController1* controller = [[NSBundle mainBundle] loadNibNamed:@"ViewController1" owner:self options:nil][0];
controller.delegate = self;
[self presentViewController:controller animated:YES completion:nil];
Теперь вы все настроены, всякий раз, когда вы обнаруживаете, что событие проверки было изменено в ViewController1, все, что вам нужно сделать, это ниже
[delegate checkStateDidChange:checked]; // You pass here YES or NO based on the check state of your control
Скажите, пожалуйста, если что-нибудь, что не понятно, если я правильно не понял ваш вопрос.
Если вы хотите отправить данные с одного на другой viewController, вот путь к нему:
Скажем, у нас есть viewControllers: viewControllerA и viewControllerB
Теперь в viewControllerB.h
@interface viewControllerB : UIViewController {
NSString *string;
NSArray *array;
}
- (id)initWithArray:(NSArray)a andString:(NSString)s;
В viewControllerB.m
#import "viewControllerB.h"
@implementation viewControllerB
- (id)initWithArray:(NSArray)a andString:(NSString)s {
array = [[NSArray alloc] init];
array = a;
string = [[NSString alloc] init];
string = s;
}
В viewControllerA.m
#import "viewControllerA.h"
#import "viewControllerB.h"
@implementation viewControllerA
- (void)someMethod {
someArray = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];
someString = [NSString stringWithFormat:@"Hahahahaha"];
viewControllerB *vc = [[viewControllerB alloc] initWithArray:someArray andString:someString];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
}
Таким образом, вы можете передавать данные с viewControllerA в viewControllerB без установки каких-либо делегатов.;)
1. Создайте экземпляр первого View Viewer во втором контроллере просмотра и сделайте его свойство @property (nonatomic,assign)
.
2. Назначьте экземпляр SecondviewController
этого контроллера представления.
2.. Когда вы закончите операцию выбора, скопируйте массив в первый вид контроллера. Когда u выгружает SecondView, FirstView будет хранить данные массива.
Надеюсь, что это поможет.
В настоящее время я участвую в решении с открытым исходным кодом этой проблемы через проект MCViewFactory, который можно найти здесь:
https://github.com/YetiHQ/manticore-iosviewfactory
Идея имитирует парадигму намерений Android, используя глобальный factory, чтобы управлять просмотром, на который вы смотрите, и использовать "намерения" для переключения и передачи данных между представлениями. Вся документация находится на странице github, но вот некоторые основные моменты:
Вы настроите все свои представления в файлах .XIB и зарегистрируете их в делегате приложения, инициализируя factory.
// Register activities
MCViewFactory *factory = [MCViewFactory sharedFactory];
// the following two lines are optional.
[factory registerView:@"YourSectionViewController"];
Теперь, в вашем VC, в любое время, когда вы хотите перейти на новый VC и передать данные, вы создаете новое намерение и добавляете данные в свой словарь (savedInstanceState). Затем просто установите текущую цель factory:
MCIntent* intent = [MCIntent intentWithSectionName:@"YourSectionViewController"];
[intent setAnimationStyle:UIViewAnimationOptionTransitionFlipFromLeft];
[[intent savedInstanceState] setObject:@"someValue" forKey:@"yourKey"];
[[intent savedInstanceState] setObject:@"anotherValue" forKey:@"anotherKey"];
// ...
[[MCViewModel sharedModel] setCurrentSection:intent];
Все ваши представления, которые соответствуют этому, должны быть подклассами MCViewController, которые позволяют вам переопределить новый метод onResume:, позволяющий вам получить доступ к переданным вами данным.
-(void)onResume:(MCIntent *)intent {
NSObject* someValue = [intent.savedInstanceState objectForKey:@"yourKey"];
NSObject* anotherValue = [intent.savedInstanceState objectForKey:@"anotherKey"];
// ...
// ensure the following line is called, especially for MCSectionViewController
[super onResume:intent];
}
Надеюсь, что некоторые из вас считают это решение полезным/интересным.
Передача данных между FirstViewController и SecondViewController ниже
Например:
Значение строки FirstViewController как
StrFirstValue = @"first";
поэтому мы можем передать это значение во втором классе, используя следующий шаг
1 > Нам нужно вырезать строковый объект в файле SecondViewController.h
NSString *strValue;
2 > Необходимо объявить свойство, как показано ниже, в файле .h.
@property (strong, nonatomic) NSString *strSecondValue;
3 > Нужно синтезировать это значение в файле FirstViewController.m ниже объявления заголовка
@synthesize strValue;
и в FirstViewController.h:
@property (strong, nonatomic) NSString *strValue;
4 > В FirstViewController, из которого мы переходим ко второму виду, напишите ниже код в этом методе.
SecondViewController *secondView= [[SecondViewController alloc]
initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]];
[secondView setStrSecondValue:StrFirstValue];
[self.navigationController pushViewController:secondView animated:YES ];
В моем случае я использовал одноэлементный класс, который может работать как глобальный объект, позволяющий получать доступ к данным почти везде в приложении. Прежде всего, нужно построить одноэлементный класс. Пожалуйста, обратитесь к странице, Каким должен быть мой Objective-C синглтон?"
И то, что я сделал, чтобы сделать объект глобально доступным, просто импортировал его в appName_Prefix.pch
, который предназначен для применения оператора импорта в каждом классе.
Чтобы получить доступ к этому объекту и использовать его, я просто внедрил метод класса для возврата общего экземпляра, который содержит свои собственные переменные
Создайте свойство в следующем view controller .h
и определите getter и setter.
Добавьте это property
в NextVC.h на nextVC
@property (strong, nonatomic) NSString *indexNumber;
Добавить
@synthesize indexNumber;
в NextVC.m
И последний
NextVC *vc=[[NextVC alloc]init];
vc.indexNumber=@"123";
[self.navigationController vc animated:YES];
Я знаю, что это избитая тема, но для тех, кто хочет ответить на этот вопрос с помощью SWIFT-косы и хочет пример с голубыми костями, вот мой метод go-to для передачи данных, если вы используете segue, чтобы обойти.
Это похоже на выше, но без кнопок, меток и т.д. Просто передавайте данные из одного представления в другое.
Настройка раскадровки
Есть три части.
Это очень простой макет представления с разделом между ними.
Вот настройка для отправителя
Вот настройка для приемника.
Наконец, настройка для сеанса.
Контроллеры просмотра
Мы сохраняем это просто, поэтому никаких кнопок, а не действий, мы просто перемещаем данные от отправителя к получателю, когда приложение загружается, а затем выводит переданное значение на консоль.
Эта страница берет первоначально загруженное значение и передает его.
//
// ViewControllerSender.swift
// PassDataBetweenViews
//
// Created by Chris Cantley on 8/25/15.
// Copyright (c) 2015 Chris Cantley. All rights reserved.
//
import UIKit
class ViewControllerSender: UIViewController {
// THE STUFF - put some info into a variable
let favoriteMovie = "Ghost Busters"
override func viewDidAppear(animated: Bool) {
// PASS IDENTIFIER - go to the recieving view controller.
self.performSegueWithIdentifier("goToReciever", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//GET REFERENCE - ...to the receiver view.
var viewControllerReceiver = segue.destinationViewController as? ViewControllerReceiver
//PASS STUFF - pass the variable along to the target.
viewControllerReceiver!.yourFavMovie = self.favoriteMovie
}
}
Эта страница просто отправляет значение переменной на консоль при ее загрузке. К этому моменту наш любимый фильм должен быть в этой переменной.
//
// ViewControllerReceiver.swift
// PassDataBetweenViews
//
// Created by Chris Cantley on 8/25/15.
// Copyright (c) 2015 Chris Cantley. All rights reserved.
//
import UIKit
class ViewControllerReceiver: UIViewController {
//Basic empty variable waiting for you to pass in your fantastic favorite movie.
var yourFavMovie = ""
override func viewDidLoad() {
super.viewDidLoad()
//And now we can view it in the console.
println("The Movie is \(self.yourFavMovie)")
}
}
Вот как вы можете справиться с этим, если хотите использовать segue, и у вас нет страниц под навигационным контроллером.
После запуска он должен автоматически переключиться на вид приемника и передать значение от отправителя к получателю, отображая значение в консоли.
Вы можете сохранить данные в делегате приложения, чтобы получить доступ к ним через контроллеры представлений в своем приложении. Все, что вам нужно сделать, это создать общий экземпляр делегата приложения
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
Пример
если вы объявляете NSArray object *arrayXYZ
, тогда вы можете получить к нему доступ в любом контроллере просмотра appDelegate.arrayXYZ
если вы хотите передать данные из ViewControlerOne в ViewControllerTwo, попробуйте эти.
Сделайте это в ViewControlerOne.h
@property (nonatomic, strong) NSString *str1;
Сделайте это в ViewControllerTwo.h
@property (nonatomic, strong) NSString *str2;
Синтезировать str2 в ViewControllerTwo.m
@interface ViewControllerTwo ()
@end
@implementation ViewControllerTwo
@synthesize str2;
Сделайте это в ViewControlerOne.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Data or string you wants to pass in ViewControllerTwo..
self.str1 = @"hello world";
}
на кнопке click event сделать это.
-(IBAction)ButtonClicked
{ //Navigation on buttons click event from ViewControlerOne to ViewControlerTwo with transferring data or string..
ViewControllerTwo *objViewTwo=[self.storyboard instantiateViewControllerWithIdentifier:@"ViewControllerTwo"];
obj.str2=str1;
[self.navigationController pushViewController: objViewTwo animated:YES];
}
сделать это в ViewControllerTwo.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"%@",str2);
}
Есть много способов сделать это, и важно выбрать правильный. Вероятно, одним из самых больших архитектурных решений является то, как код модели будет доступен или доступен во всем приложении.
Я написал сообщение в блоге об этом некоторое время назад: Совместное использование кода модели. Вот краткое резюме:
Один из подходов - обмениваться указателями на объекты модели между контроллерами представлений.
Поскольку подготовка к segue является наиболее распространенной, вот пример:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var next = segue.destinationViewController as NextViewController
next.dataSource = dataSource
}
Другой подход заключается в том, чтобы обрабатывать экран, полный данных за один раз, и вместо того, чтобы связывать контроллеры представлений друг с другом, каждая из контроллеров представления с одним источником данных, который они могут получить независимо.
Самый распространенный способ, которым я видел это, - это одиночный экземпляр. Поэтому, если ваш объект singleton был DataAccess
вы можете сделать следующее в методе viewDidLoad UIViewController:
func viewDidLoad() {
super.viewDidLoad()
var data = dataAccess.requestData()
}
Существуют дополнительные инструменты, которые также помогают передавать данные:
Хорошая вещь в Core Data заключается в том, что она имеет обратные отношения. Поэтому, если вы хотите просто предоставить NotesViewController объект заметок, который вы можете использовать, поскольку он будет иметь обратную связь с чем-то другим, например, с ноутбуком. Если вам нужны данные в записной книжке в NotesViewController, вы можете вернуться к графику объекта, выполнив следующие действия:
let notebookName = note.notebook.name
Подробнее об этом читайте в моем блоге: общий код модели
NewsViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tbl_View deselectRowAtIndexPath:indexPath animated:YES];
News *newsObj = [newstitleArr objectAtIndex:indexPath.row];
NewsDetailViewController *newsDetailView = [[NewsDetailViewController alloc] initWithNibName:@"NewsDetailViewController" bundle:nil];
newsDetailView.newsHeadlineStr = newsObj.newsHeadline;
[self.navigationController pushViewController:newsDetailView animated:YES];
}
NewsDetailViewController.h
@interface NewsDetailViewController : UIViewController
@property(nonatomic,retain) NSString *newsHeadlineStr;
@end
NewsDetailViewController.m
@synthesize newsHeadlineStr;
Делегирование - единственное решение для выполнения таких операций, когда вы используете .xib файлы, однако все ответы, описанные выше, для storyboard
для .xibs файлов, вам нужно использовать делегирование. это единственное решение, которое вы можете.
Другим решением является использование singleton class pattern, инициализирующее его один раз и использование его во всем приложении.
Мне нравится идея объектов модели и объектов Mock на основе NSProxy для фиксации или удаления данных, если пользовательский выбор может быть отменен.
Легко передавать данные, поскольку это один объект или пара объектов, и если вы позволили использовать контроллер UINavigationController, вы можете сохранить ссылку на модель внутри, и все нажатые контроллеры представлений могут получить к ней доступ непосредственно от контроллера навигации.
Если вы хотите отправить данные с одного на другой viewController, вот путь к нему:
Скажем, у нас есть viewControllers: ViewController и NewViewController.
в ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
{
IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4;
}
@property (nonatomic,retain) IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4;
-(IBAction)goToNextScreen:(id)sender;
@end
в ViewController.m
#import "ViewController.h"
#import "NewViewController.h"
@implementation ViewController
@synthesize mytext1,mytext2,mytext3,mytext4;
-(IBAction)goToNextScreen:(id)sender
{
NSArray *arr = [NSArray arrayWithObjects:mytext1.text,mytext2.text,mytext3.text,mytext4.text, nil];
NewViewController *newVc = [[NewViewController alloc] initWithNibName:@"NewViewController" bundle:nil];
newVc.arrayList = arr;
[self.navigationController pushViewController:newVc animated:YES];
}
В NewViewController.h
#import <UIKit/UIKit.h>
@interface NewViewController : UITableViewController
{
NSArray *arrayList;
NSString *name,*age,*dob,*mobile;
}
@property(nonatomic, retain)NSArray *arrayList;
@end
В NewViewController.m
#import "NewViewController.h"
#import "ViewController.h"
@implementation NewViewController
@synthesize arrayList;
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [arrayList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.textLabel.text = [arrayList objectAtIndex:indexPath.row];
return cell;
}
@end
Таким образом, мы можем передавать данные с одного диспетчера представлений на другой контроллер представления...
Я видел много людей, которые усложняли это, используя метод didSelectRowAtPath
. Я использую Core Data в моем примере.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//this solution is for using Core Data
YourCDEntityName * value = (YourCDEntityName *)[[self fetchedResultsController] objectAtIndexPath: indexPath];
YourSecondViewController * details = [self.storyboard instantiateViewControllerWithIdentifier:@"nameOfYourSecondVC"];//make sure in storyboards you give your second VC an identifier
//Make sure you declare your value in the second view controller
details.selectedValue = value;
//Now that you have said to pass value all you need to do is change views
[self.navigationController pushViewController: details animated:YES];
}
4 строки кода внутри метода, и все готово.
Существует три типа передачи данных один ViewController в другой ViewController.
Ссылка на демонстрационный проект здесь - https://github.com/kamanijasmin13/Swift-Pass-data-between-viewcontrollers
Программно
Segue
UserDefaults
Ссылка на демонстрационный проект здесь - https://github.com/kamanijasmin13/Swift-Pass-data-between-viewcontrollers
Есть много ответов на эти вопросы, предлагающие множество различных способов взаимодействия с диспетчером диспетчера сообщений, которые действительно работают, но я не вижу нигде не упомянутых, какие из них лучше всего использовать и какие из них следует избегать.
prepare(for:sender:)
UIViewController
при использовании раскадровки и seguesЭти решения, хотя и работают в краткосрочной перспективе, вводят слишком много зависимостей, которые будут искажать архитектуру приложения и впоследствии создавать больше проблем.
Для тех, кого это интересует, я написал несколько статей, которые более подробно затрагивают эти моменты и выделяют различные недостатки:
Это действительно замечательный tutorial для всех, кто этого хочет. Вот пример кода:
- (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];
}
}
ИСПОЛЬЗОВАНИЕ ЦЕНТРА УВЕДОМЛЕНИЙ
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
}
}
}
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
}
}
}
[AnyHashable: Any]
вместо NSDictionary. Кстати, stackoverflow.com/questions/30328452/…
для отправки данных от одного VC к другому используйте этот простой подход:
YourNextVC *nxtScr = (YourNextVC*)[self.storyboard instantiateViewControllerWithIdentifier:@"YourNextVC"];//Set this identifier from your storyboard
nxtScr.comingFrom = @"PreviousScreen"l
[self.navigationController nxtScr animated:YES];
@property (strong,nonatomic) NSString *comingFrom;
у нас есть несколько способов, которыми мы можем работать с системой делегатов или с помощью раскадровки.
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];
}
}
Теперь я успешно получил строку pdf url и другой ViewController и использую эту строку в webview...
2- Работая с такими делегатами, у меня есть один класс утилит NSObject, содержащий мои методы dateFormatter, sharedInstance, EscapeWhiteSpaceCharacters, convertImageToGrayScale и еще один метод, с которым я работал с помощью приложения, поэтому теперь в утилитах.h
в этом вам не нужно создавать переменные при вечном анализе времени от одного к другому контроллеру представления один раз, когда ваша созданная строковая переменная в utilities.h просто делает ее нулевой; и снова используется
@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
}
эта работа делегата является надежной с управлением памятью
Вы можете создать push segue из source viewcontroller в диспетчер view view и дать имя идентификатора, как показано ниже.
Вы должны выполнитьsegegue от didselectRowAt, как это.
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 целевого диспетчера представлений, а затем сохранить его в базе данных.
override func viewDidLoad{
if val != ""{
btnSelectCategory.setTitle(val, for: .normal)
}
}
Я рекомендую блоки/блокировки и настраиваемые конструкторы.
Предположим, вам нужно передать строку из 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)
}
}
Второй контроллер просмотра
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")
}
}
Способ Apple для этого - использовать Segues. Вам нужно использовать функцию prepareForSegue()
Есть много отличных уроков, вот один из них: https://www.iphonelife.com/content/unleash-your-inner-app-developer-part-21-passing-data-between-controllers
Кроме того, прочитайте документы Apple по использованию segues: https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/UsingSegues.html
Я предпочитаю делать это без делегатов и 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?()
}
}
Вы должны всегда следовать концепции MVC при создании приложений для iOS. Существует два сценария, в которых вы можете передавать данные с ViewController на другой:
Когда в иерархии есть "A" ViewContoller, и вы хотите отправить некоторые данные в "B", который является следующим диспетчером представлений. В этом случае вы должны использовать Segue. Просто установите идентификатор для 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
}
}
Когда есть A
который открыл B
на себе как модальный (или встроенный). Теперь диспетчер представлений B
должен быть ослеплен относительно этого родителя. Таким образом, лучший способ отправить данные обратно в A
- это использовать Delegation
. Создайте протокол делегата в B
viewcontroller и свойство delegate
. Таким образом, B
будет сообщать (отправлять данные обратно) делегату. В A
viewcontroller мы реализуем протокол делегата B
viewcontroller и устанавливаем self
как свойство delegate
B
viewcontroller в режиме prepare(forSegue:)
.
Вот как это должно быть выполнено правильно. Надеюсь, поможет
Простой способ здесь.
Просто используйте глобальную переменную. Объявите объект или переменную, необходимую для перехода к следующему классу.
Например, у нас есть 2 класса - classA
и classB
соответственно.
В classA
обычно содержит:
#import "classA.h"
@interface classA()
@end
@implementation classA
-(void)viewDidLoad
{
...
}
-(void)didReceiveMemoryWarning
{
...
}
и classB
содержит:
#import "classB.h"
@interface classB()
@end
@implementation classB
-(void)viewWillLoad
{
...
}
-(void)didReceiveMemoryWarning
{
...
}
Теперь импортируйте второй класс classB
в classA
#import "classA.h"
#import "classB.h" //---import classB to classA.
@interface classA()
@end
@implementation classA
-(void)viewDidLoad
{
...
}
-(void)didReceiveMemoryWarning
{
...
}
Теперь у нас есть мост, чтобы перейти ко второму классу classB
теперь, для объявления переменной или объекта как глобального, объявите его в .m файле первого класса, как показано ниже
В classA.h
#import "classA.h"
#import "classB.h"
@interface classA()
@end
NSString *temp; //----declare any object/variable as global.
@implementation classA
-(void)viewDidLoad
{
...
temp=@"Hello";
...
}
-(void)didReceiveMemoryWarning
{
...
}
Здесь объект temp
является глобальным объектом класса NSString
для доступа к глобальному объекту или переменной в любом классе, просто переопределяйте объект или переменную во втором классе. Например. ниже:
В classB.m
#import "classB.h"
@interface classB()
@end
extern NSString *temp; //----use `extern` keyword for using the global object/variable in classB that was declared in classA.
@implementation classB
-(void)viewDidLoad
{
...
LabeL.text=temp;
...
}
-(void)didReceiveMemoryWarning
{
...
}
Теперь значение может быть получено вторым классом. Простой!.. Этот метод может применяться для любого количества классов.
Примечание:
Вы должны импортировать .h файл второго класса в первый класс. Но нет необходимости импортировать .h файл первого класса для второго класса.
Помните мост, если есть мост, он должен быть в состоянии идти с обеих сторон.
Я думаю, это поможет. Это помогло мне, когда я был в той же ситуации.
Использовать центр уведомлений для передачи данных с одного вида на другой. Модель прослушивателя наблюдателя работает наилучшим образом. Другая работа вокруг может создавать те же объекты как в классе, Создайте объект класса 2 в классе 1 для доступа к объектам данных, которые необходимо передать, и установите их, а затем нажмите контроллер представления.