Przekazywanie danych między kontrolerami widoku

1372

Jestem nowy w iOS i Objective-C oraz całym paradygmacie MVC i utknąłem w następujących kwestiach:

Mam widok, który działa jak formularz wprowadzania danych i chcę dać użytkownikowi możliwość wyboru wielu produktów. Produkty są wymienione w innym widoku, UITableViewControllera ja włączyłem wiele opcji.

Moje pytanie brzmi: jak przenieść dane z jednego widoku do drugiego? Będę trzymał wybory UITableVieww tablicy, ale jak mam przekazać to z powrotem do widoku formularza wprowadzania danych, aby można go było zapisać wraz z innymi danymi do Core Data po przesłaniu formularza?

Surfowałem i widziałem, jak niektórzy ludzie deklarują tablicę w delegacie aplikacji. Czytam coś o Singletonach, ale nie rozumiem, co to są i czytam o tworzeniu modelu danych.

Jaki byłby właściwy sposób na wykonanie tego i jak mam to zrobić?

Cena matowa
źródło

Odpowiedzi:

1683

To pytanie wydaje się być bardzo popularne tutaj w stackoverflow, więc pomyślałem, że postaram się udzielić lepszej odpowiedzi, aby pomóc osobom rozpoczynającym pracę w świecie iOS, takim jak ja.

Mam nadzieję, że ta odpowiedź jest wystarczająco jasna, aby ludzie mogli ją zrozumieć i że niczego mi nie umknęło.

Przekazywanie danych do przodu

Przekazywanie danych do kontrolera widoku z innego kontrolera widoku. Zastosowałbyś tę metodę, gdybyś chciał przekazać obiekt / wartość z jednego kontrolera widoku do innego kontrolera widoku, który możesz naciskać na stos nawigacji.

W tym przykładzie będziemy mieli ViewControllerAiViewControllerB

Aby przekazać BOOLwartość od ViewControllerAdo ViewControllerB, wykonajmy następujące czynności.

  1. w ViewControllerB.hutwórz właściwość dlaBOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
  2. w ViewControllerAmusisz powiedzieć o tym, ViewControllerBwięc użyj

    #import "ViewControllerB.h"

    Następnie, gdzie chcesz załadować widok np. didSelectRowAtIndexlub niektóre IBActionmusisz ustawić właściwość ViewControllerBprzed wypchnięciem jej na stos nawigacyjny.

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

    Spowoduje to ustawienie isSomethingEnabledw ViewControllerBcelu BOOLwartości YES.

Przekazywanie danych za pomocą segmentów

Jeśli używasz Storyboardów, najprawdopodobniej używasz sekwensów i będziesz potrzebować tej procedury, aby przekazać dane dalej. Jest to podobne do powyższego, ale zamiast przekazywać dane przed naciśnięciem kontrolera widoku, używasz metody o nazwie

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

Aby przekazać BOOLod ViewControllerAdo ViewControllerB, zrobilibyśmy następujące:

  1. w ViewControllerB.hutwórz właściwość dlaBOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
  2. w ViewControllerAmusisz powiedzieć o tym, ViewControllerBwięc użyj

    #import "ViewControllerB.h"
  3. Utwórz segregację od ViewControllerAdo ViewControllerBna storyboardzie i podaj jej identyfikator, w tym przykładzie nazwiemy go"showDetailSegue"

  4. Następnie musimy dodać metodę, ViewControllerAktóra jest wywoływana, gdy wykonywany jest jakikolwiek segment, dlatego musimy wykryć, który segment został wywołany, a następnie coś zrobić. W naszym przykładzie sprawdzimy, "showDetailSegue"a jeśli zostanie to wykonane, przekażemy naszą BOOLwartość doViewControllerB

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

    Jeśli masz widoki osadzone w kontrolerze nawigacyjnym, musisz nieco zmienić powyższą metodę na następującą

    -(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;
        }
    }

    Spowoduje to ustawienie isSomethingEnabledw ViewControllerBcelu BOOLwartości YES.

Przekazywanie danych z powrotem

Aby przekazać dane z powrotem ViewControllerBdo ViewControllerA, musisz użyć protokołów i delegatów lub bloków , ten ostatni może być używany jako luźno powiązany mechanizm oddzwaniania.

W tym celu zostaniemy ViewControllerAdelegatem ViewControllerB. Pozwala ViewControllerBto odesłać wiadomość z powrotem, ViewControllerAumożliwiając nam odesłanie danych z powrotem.

Aby ViewControllerAzostać delegatem ViewControllerB, musi być zgodny z ViewControllerBprotokołem, który musimy określić. Mówi to, ViewControllerAktóre metody musi wdrożyć.

  1. W ViewControllerB.h, poniżej #import, ale powyżej @interfaceokreślasz protokół.

    @class ViewControllerB;
    
    @protocol ViewControllerBDelegate <NSObject>
    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
    @end
  2. następnie nadal w ViewControllerB.hmusisz ustawić delegatewłaściwość i dokonać syntezyViewControllerB.m

    @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
  3. W ViewControllerBwywołujemy komunikat o tym, delegatekiedy otwieramy kontroler widoku.

    NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
  4. To tyle ViewControllerB. Teraz w ViewControllerA.h, powiedz, ViewControllerAaby zaimportować ViewControllerBi dostosować się do protokołu.

    #import "ViewControllerB.h"
    
    @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
  5. W ViewControllerA.mrealizować w następujący sposób z naszym protokołem

    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
    {
        NSLog(@"This was returned from ViewControllerB %@",item);
    }
  6. Przed viewControllerBprzekazaniem do stosu nawigacyjnego musimy powiedzieć, ViewControllerBże ViewControllerAjest jego delegatem, w przeciwnym razie otrzymamy błąd.

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

Bibliografia

  1. Używanie delegowania do komunikowania się z innymi kontrolerami widoku w Przewodniku programowania kontrolera widoku
  2. Deleguj wzór

NSNotification center To kolejny sposób przekazywania danych.

// 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];

Przekazywanie danych z jednej klasy do drugiej (klasą może być dowolny kontroler, menedżer sieci / sesji, podklasa UIView lub dowolna inna klasa)

Bloki są funkcjami anonimowymi.

Ten przykład przekazuje dane z kontrolera B do kontrolera A.

zdefiniuj blok

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

dodaj moduł obsługi bloku (detektor) tam, gdzie potrzebujesz wartości (na przykład potrzebujesz odpowiedzi API w ControllerA lub potrzebujesz danych ContorllerB w A)

// in ContollerA.m

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

Przejdź do kontrolera B.

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

blok ognia

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

Kolejny przykład roboczy dla bloków

Cena matowa
źródło
24
Czy musimy również umieścić @class ViewControllerB;powyższą definicję @protocol? Bez niego pojawia się błąd „Oczekiwany typ” na ViewControllerB w wierszu: - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item; w @protocoldeklaracji
alan-p
4
To działa świetnie. Jak mówi alan-p, nie zapomnij napisać @class ViewControllerB; powyżej protokołu, w przeciwnym razie pojawi się błąd „Oczekiwany typ”.
Andrew Davis,
6
nie potrzebujesz delegatów do przekazywania, po prostu skorzystaj z odprężenia.
malhal
4
Kiedy wstawiam „viewControllerB.delegate = self;” w ViewControllerB pojawia się błąd. Przypisując do „id <ViewControllerBDelegate>” z niezgodnego typu „ViewControllerB * const __strong”, nie jestem pewien, co robię źle. Czy ktoś może pomóc? Dodatkowo musiałem zmienić: initWithNib -> initWithNibName.
uplearnedu.com
4
jeśli używasz NavigationController, musisz użyć [self.navigationController pushViewController:viewController animated:YES];zamiast tego[self pushViewController:viewControllerB animated:YES];
Nazir
192

Szybki

Istnieje mnóstwo tonów wyjaśnień tutaj i wokół StackOverflow, ale jeśli jesteś początkującym, próbując zdobyć coś podstawowego do pracy, spróbuj obejrzeć ten samouczek YouTube (dzięki temu mogłem w końcu zrozumieć, jak to zrobić).

Przekazywanie danych do następnego kontrolera widoku

Poniżej znajduje się przykład oparty na filmie. Pomysł polega na przekazaniu ciągu znaków z pola tekstowego w Pierwszym kontrolerze widoku do etykiety w drugim kontrolerze widoku.

wprowadź opis zdjęcia tutaj

Utwórz układ scenorysu w Konstruktorze interfejsów. Aby zrobić segue, wystarczy Controlkliknąć przycisk i przeciągnąć do kontrolera drugiego widoku.

Kontroler pierwszego widoku

Kod kontrolera First View to

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!
    }

}

Kontroler drugiego widoku

Kod drugiego kontrolera widoku to

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
    }

}

Nie zapomnij

  • Podłącz punkty dla UITextFieldi UILabel.
  • Ustaw pierwszy i drugi kontroler widoku na odpowiednie pliki Swift w IB.

Przekazywanie danych z powrotem do poprzedniego kontrolera widoku

Aby przekazać dane z drugiego kontrolera widoku do pierwszego kontrolera widoku, użyj protokołu i delegata . Ten film jest jednak bardzo przejrzystym przykładem tego procesu:

Poniżej znajduje się przykład oparty na filmie (z kilkoma modyfikacjami).

wprowadź opis zdjęcia tutaj

Utwórz układ scenorysu w Konstruktorze interfejsów. Ponownie, aby zrobić segue, po prostu Controlprzeciągnij z przycisku do kontrolera drugiego widoku. Ustaw identyfikator segue na showSecondViewController. Nie zapomnij także podłączyć gniazdek i działań, używając nazw w poniższym kodzie.

Kontroler pierwszego widoku

Kod kontrolera First View to

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
    }
}

Zwróć uwagę na użycie naszego niestandardowego DataEnteredDelegateprotokołu.

Drugi widok kontrolera i protokołu

Kod drugiego kontrolera widoku to

import UIKit

// protocol used for sending data back
protocol DataEnteredDelegate: AnyObject {
    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)
    }
}

Zauważ, że protocoljest poza klasą View Controller.

Otóż ​​to. Uruchomiona aplikacja powinna być w stanie odesłać dane z drugiego kontrolera widoku do pierwszego.

Suragch
źródło
Biorąc pod uwagę niektóre najnowsze aktualizacje Swift, czy nadal jest to powszechny wzorzec do wdrożenia?
piofusco,
4
Większość wszystkich aktualizacji Swift, które widziałem, były względnie niewielkimi zmianami składniowymi, a nie zmianami w sposobie przekazywania danych między kontrolerami widoku. Jeśli dowiem się o takich poważnych zmianach, zaktualizuję swoją odpowiedź.
Suragch,
2
offtopic - iOS ma tak brzydki sposób przekazywania parametrów do nowych kontrolerów widoku, niewiarygodne - musisz ustawić parametry nie w miejscu, w którym dzwonisz, ale w innym. Android ma lepsze podejście pod tym względem - po rozpoczęciu działania możesz przekazać dowolne dane (dobrze, prawie) za pośrednictwem początkowej intencji. Łatwy. Nie musisz rzucać ani nic takiego. Przekazywanie wartości zwracanych z powrotem do osoby dzwoniącej jest również istotną rzeczą, bez konieczności delegowania. Oczywiście możliwe jest również stosowanie brzydkich podejść, nie ma problemu))
Mixaz
1
@Himanshu, najpierw uzyskaj odniesienie do drugiego kontrolera widoku. Następnie zaktualizuj zawartą w nim zmienną publiczną.
Suragch
8
@Miód. Myślę, że słowo „delegat” jest mylące. Pozwól, że użyję słowa „pracownik”. „Pracownik” (pierwszy kontroler widoku) robi wszystko, co nakazuje mu „szef” (drugi kontroler widoku). „Szef” nie wie, kim będzie jego „pracownik”; to może być każdy. Tak więc w pierwszym kontrolerze widoku (klasa „robotnicza”) jest napisane, że będę twoim „robotnikiem”. Powiedz mi, co mam napisać na etykiecie, a zrobię to dla ciebie. Oznacza secondViewController.delegate = selfto zatem: „Zgadzam się być szefem”. Zobacz tę odpowiedź na inny przykład i więcej wyjaśnień.
Suragch
136

M w MVC dotyczy „Modelu”, aw paradygmacie MVC rolą klas modeli jest zarządzanie danymi programu. Model jest przeciwieństwem widoku - widok wie, jak wyświetlać dane, ale nie wie nic o tym, co zrobić z danymi, podczas gdy model wie wszystko o tym, jak pracować z danymi, ale nic o tym, jak je wyświetlić. Modele mogą być skomplikowane, ale nie muszą tak być - model aplikacji może być tak prosty, jak tablica ciągów lub słowników.

Rolą kontrolera jest pośredniczenie między widokiem a modelem. Dlatego potrzebują odniesienia do jednego lub więcej obiektów widoku i jednego lub więcej obiektów modelu. Załóżmy, że Twój model to tablica słowników, w której każdy słownik reprezentuje jeden wiersz w tabeli. Widok główny aplikacji wyświetla tę tabelę i może być odpowiedzialny za ładowanie tablicy z pliku. Gdy użytkownik zdecyduje się dodać nowy wiersz do tabeli, stuknie jakiś przycisk, a kontroler utworzy nowy (modyfikowalny) słownik i doda go do tablicy. Aby wypełnić wiersz, kontroler tworzy kontroler widoku szczegółów i nadaje mu nowy słownik. Kontroler widoku szczegółowego wypełnia słownik i zwraca. Słownik jest już częścią modelu, więc nic więcej nie musi się wydarzyć.

Caleb
źródło
95

Istnieją różne sposoby odbioru danych do innej klasy w systemie iOS. Na przykład -

  1. Bezpośrednia inicjalizacja po przydzieleniu innej klasy.
  2. Delegowanie - do przekazywania danych z powrotem
  3. Powiadomienie - do rozgłaszania danych do wielu klas jednocześnie
  4. Zapisywanie NSUserDefaults - do późniejszego dostępu
  5. Lekcje singletonu
  6. Bazy danych i inne mechanizmy przechowywania, takie jak plist itp.

Jednak w przypadku prostego scenariusza przekazania wartości do innej klasy, której alokacji dokonuje się w bieżącej klasie, najbardziej powszechną i preferowaną metodą byłoby bezpośrednie ustawienie wartości po alokacji. Odbywa się to w następujący sposób:

Możemy to zrozumieć za pomocą dwóch kontrolerów - Controller1 i Controller2

Załóżmy, że w klasie Controller1 chcesz utworzyć obiekt Controller2 i przekazać go z przekazywaną wartością typu String. Można to zrobić w następujący sposób:

- (void)pushToController2 {

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

W implementacji klasy Controller2 pojawi się ta funkcja jako-

@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

Możesz także bezpośrednio ustawić właściwości klasy Controller2 w podobny sposób:

- (void)pushToController2 {

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

Aby przekazać wiele wartości, możesz użyć wielu parametrów, takich jak:

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

Lub jeśli potrzebujesz przekazać więcej niż 3 parametry związane ze wspólną funkcją, możesz zapisać wartości do klasy Model i przekazać ten modelObject do następnej klasy

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];

Krótko mówiąc, jeśli chcesz -

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.

Mam nadzieję że to pomoże

borncrazy
źródło
84

Po dalszych badaniach wydawało się, że Protokoły i delegaci to właściwy / preferowany przez Apple sposób robienia tego.

Skończyło się na tym przykładzie

Udostępnianie danych między kontrolerami widoku a innymi obiektami @ iPhone Dev SDK

Działa dobrze i pozwala mi przekazywać ciąg i tablicę do przodu i do tyłu między moimi widokami.

Dziękuję za twoją pomoc

Cena matowa
źródło
3
nie używaj protokołów i delegatów, po prostu odpocznij.
malhal
1
@malhal Co zrobić, jeśli nie używasz scenariuszy?
Evan R
Nienawidzę też bezużytecznych protokołów i delegatów. @malhal
DawnSong
@EvanR Możesz tworzyć i wykonywać sekwencje w kodzie. To wszystko jest takie samo.
DawnSong,
1
Zasadniczo cała kontrola jakości na tej stronie jest „z dawnych czasów przed wyświetleniem kontenera”. Nigdy od miliona lat nie zawracasz sobie głowy protokołami ani delegatami. Zresztą każda drobna rzecz, którą robisz na dowolnym ekranie, jest widokiem kontenera, więc pytanie naprawdę już nie istnieje - masz już wszystkie odniesienia „w górę i w dół” ze wszystkich widoków kontenera.
Fattie
66

Znajduję najprostszą i najbardziej elegancką wersję z przechodzącymi blokami. Nazwijmy kontroler widoku, który czeka na zwrócone dane, na „A”, a kontroler widoku na „B”. W tym przykładzie chcemy uzyskać 2 wartości: pierwszą z Type1 i drugą z Type2.

Zakładając, że używamy Storyboard, pierwszy kontroler ustawia blok zwrotny, na przykład podczas przygotowywania 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's do some action after with returned values
            action1(value1);
            action2(value2);
        };

    }
}

i kontroler widoku „B” powinien zadeklarować właściwość wywołania zwrotnego BViewController.h:

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

Niż w pliku implementacyjnym BViewController.m po tym, jak żądamy wartości do zwrócenia, nasze wywołanie zwrotne powinno zostać wywołane:

if (self.callback)
    self.callback(value1, value2);

Jedną rzeczą do zapamiętania jest to, że używanie bloku często musi zarządzać silnymi i __słabymi referencjami, jak wyjaśniono tutaj

Leszek Zarna
źródło
Dlaczego warto nie być parametrem bloku zwrotnego, a nie oddzielną właściwością?
Timuçin,
56

W wielu udzielonych odpowiedziach jest kilka dobrych informacji, ale żadne z nich nie odnosi się w pełni do pytania.

Pytanie dotyczy przekazywania informacji między kontrolerami widoku. Podany konkretny przykład dotyczy przekazywania informacji między widokami, ale biorąc pod uwagę stwierdzoną nowość w systemie iOS, oryginalny plakat prawdopodobnie oznaczał między viewControllers, a nie między widokami (bez żadnego udziału ViewControllers). Wygląda na to, że wszystkie odpowiedzi koncentrują się na dwóch kontrolerach widoku, ale co, jeśli aplikacja ewoluuje i musi wymagać więcej niż dwóch kontrolerów widoku w wymianie informacji?

Oryginalny plakat zapytał także o Singletons i korzystanie z AppDelegate . Na te pytania należy odpowiedzieć.

Aby pomóc każdemu, kto patrzy na to pytanie, kto chce pełnej odpowiedzi, postaram się udzielić jej.

Scenariusze aplikacji

Zamiast mieć wysoce hipotetyczną, abstrakcyjną dyskusję, pomaga mieć na uwadze konkretne zastosowania. Aby pomóc w zdefiniowaniu sytuacji kontrolera dwóch widoków i sytuacji kontrolera więcej niż dwóch widoków, zdefiniuję dwa konkretne scenariusze aplikacji.

Scenariusz pierwszy: maksymalnie dwa kontrolery widoku muszą kiedykolwiek wymieniać informacje. Zobacz schemat pierwszy.

schemat pierwotnego problemu

Aplikacja zawiera dwa kontrolery widoku. Istnieje ViewControllerA (formularz wprowadzania danych) i View Controller B (lista produktów). Elementy wybrane na liście produktów muszą pasować do elementów wyświetlanych w polu tekstowym w formularzu wprowadzania danych. W tym scenariuszu ViewControllerA i ViewControllerB muszą komunikować się ze sobą bezpośrednio, a nie z innymi kontrolerami widoku.

Scenariusz drugi : więcej niż dwa kontrolery widoku muszą udostępniać te same informacje. Zobacz diagram drugi.

schemat zastosowania inwentarza domowego

Aplikacja zawiera cztery kontrolery widoku. Jest to aplikacja oparta na kartach do zarządzania zapasami domowymi. Trzy kontrolery widoków przedstawiają różnie filtrowane widoki tych samych danych:

  • ViewControllerA - przedmioty luksusowe
  • ViewControllerB - Produkty nieubezpieczone
  • ViewControllerC - Całe wyposażenie domu
  • ViewControllerD - Dodaj formularz nowej pozycji

Za każdym razem, gdy pojedynczy element jest tworzony lub edytowany, musi również zostać zsynchronizowany z innymi kontrolerami widoku. Na przykład, jeśli dodamy łódź w ViewControllerD, ale nie jest ona jeszcze ubezpieczona, to łódź musi pojawić się, gdy użytkownik przejdzie do ViewControllerA (przedmioty luksusowe), a także ViewControllerC (cały asortyment domowy), ale nie wtedy, gdy użytkownik przejdzie do ViewControllerB (Produkty nieubezpieczone). Musimy martwić się nie tylko dodawaniem nowych elementów, ale także usuwaniem elementów (które mogą być dozwolone z dowolnego z czterech kontrolerów widoku) lub edytowaniem istniejących elementów (które mogą być dozwolone z poziomu „formularza Dodaj nowy element”, zmieniając przeznaczenie tego samego do edycji).

Ponieważ wszystkie kontrolery widoku muszą współużytkować te same dane, wszystkie cztery kontrolery widoku muszą pozostać zsynchronizowane, a zatem musi istnieć pewnego rodzaju komunikacja z wszystkimi innymi kontrolerami widoku, ilekroć dowolny pojedynczy kontroler widoku zmienia dane bazowe. Powinno być dość oczywiste, że nie chcemy, aby każdy kontroler widoku komunikował się bezpośrednio ze sobą w tym scenariuszu. Jeśli nie jest to oczywiste, zastanów się, czy mieliśmy 20 różnych kontrolerów widoku (a nie tylko 4). Jak trudne i podatne byłoby na powiadamianie każdego z pozostałych 19 kontrolerów widoku za każdym razem, gdy jeden kontroler widoku dokonał zmiany?

Rozwiązania: delegaci i wzorzec obserwatora oraz singletony

W pierwszym scenariuszu mamy kilka realnych rozwiązań, które dały inne odpowiedzi

  • twierdzi
  • delegaci
  • bezpośrednie ustawianie właściwości kontrolerów widoku
  • NSUserDefaults (właściwie zły wybór)

W scenariuszu drugim mamy inne realne rozwiązania:

  • Wzór obserwatora
  • Singletony

Pojedyncza jest przykładem klasy, który przykładowo jest wystąpienie tylko istnieje podczas jego użytkowania. Singleton bierze swoją nazwę od faktu, że jest to pojedyncza instancja. Zwykle programiści używający singletonów mają specjalne metody dostępu do nich.

+ (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;
}

Teraz, gdy rozumiemy, czym jest singleton, porozmawiajmy o tym, jak singleton pasuje do wzorca obserwatora. Wzorzec obserwatora służy do tego, aby jeden obiekt reagował na zmiany wprowadzane przez inny obiekt. W drugim scenariuszu mamy czterech różnych kontrolerów widoku, którzy wszyscy chcą wiedzieć o zmianach w podstawowych danych. „Podstawowe dane” powinny należeć do pojedynczej instancji, singletonu. „Wiedzieć o zmianach” dokonuje się, obserwując zmiany dokonane w singletonie.

Aplikacja do inwentaryzacji domowej miałaby jedną instancję klasy zaprojektowanej do zarządzania listą pozycji inwentarza. Kierownik zarządzałby kolekcją artykułów gospodarstwa domowego. Poniżej znajduje się definicja klasy dla menedżera danych:

#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

Kiedy zmienia się kolekcja artykułów gospodarstwa domowego, należy poinformować kontrolerów widoku o tej zmianie. Powyższa definicja klasy nie wyjaśnia, jak to się stanie. Musimy podążać za wzorem obserwatora. Kontrolery widoku muszą formalnie obserwować sharedManager. Istnieją dwa sposoby zaobserwowania innego obiektu:

  • Obserwacja kluczowych wartości (KVO)
  • NSNotificationCenter.

W scenariuszu drugim nie mamy jednej właściwości HouseholdInventoryManager, którą można zaobserwować za pomocą KVO. Ponieważ nie mamy jednej właściwości, którą można łatwo zaobserwować, wzorzec obserwatora w tym przypadku musi zostać zaimplementowany za pomocą NSNotificationCenter. Każdy z czterech kontrolerów widoku subskrybowałby powiadomienia, a SharedManager wysyłałby powiadomienia do centrum powiadomień, gdy było to właściwe. Menedżer zapasów nie musi nic wiedzieć o kontrolerach widoku ani instancjach innych klas, które mogą być zainteresowane wiedzieć, kiedy zmienia się kolekcja przedmiotów zapasowych; NSNotificationCenter zajmuje się tymi szczegółami implementacji. Kontrolery widoku po prostu subskrybują powiadomienia, a menedżer danych po prostu publikuje powiadomienia.

Wielu początkujących programistów korzysta z faktu, że zawsze jest dokładnie jeden delegat aplikacji w czasie życia aplikacji, który jest ogólnie dostępny. Początkujący programiści wykorzystują ten fakt do umieszczania obiektów i funkcji w aplikacji Delegate jako wygody dostępu z dowolnego miejsca w aplikacji. To, że AppDelegate jest singletonem, nie oznacza, że ​​powinno zastąpić wszystkie inne singletony. Jest to zła praktyka, ponieważ zbytnio obciąża jedną klasę, łamiąc dobre praktyki obiektowe. Każda klasa powinna odgrywać wyraźną rolę, którą można łatwo wyjaśnić, często tylko nazwą klasy.

Za każdym razem, gdy delegat aplikacji zaczyna się wzdęcia, zacznij usuwać funkcje z singletonów. Na przykład stos danych podstawowych nie powinien pozostać w AppDelegate, ale powinien zostać umieszczony we własnej klasie, klasie coreDataManager.

Bibliografia

Jason Cross
źródło
41

OP nie wspominał o kontrolerach widoku, ale robi tak wiele odpowiedzi, że chciałem zapoznać się z tym, co niektóre z nowych funkcji LLVM pozwalają to ułatwić, gdy chcemy przekazać dane z jednego kontrolera widoku do drugiego, a następnie odzyskanie niektórych wyników.

Sekwencje scenorysu, bloki ARC i LLVM sprawiają, że jest to dla mnie łatwiejsze niż kiedykolwiek. Niektóre odpowiedzi powyżej wspomniane scenorysy i sekwencje już były, ale nadal polegały na delegacji. Definiowanie delegatów z pewnością działa, ale niektórym osobom może łatwiej przekazywać wskaźniki lub bloki kodu.

Dzięki UINavigators i segu istnieją proste sposoby przekazywania informacji do kontrolera podrzędnego i odzyskiwania informacji. ARC sprawia, że ​​przekazywanie wskaźników do rzeczy pochodzących z obiektów NSObjects jest proste, więc jeśli chcesz, aby kontroler podrzędny dodał / zmienił / zmodyfikował niektóre dane za ciebie, przekaż go wskaźnikiem do zmiennej instancji. Bloki ułatwiają przekazywanie akcji, więc jeśli chcesz, aby kontroler podrzędny wywoływał akcję na kontrolerze wyższego poziomu, przekaż mu blok. Definiujesz blok, aby akceptować dowolną liczbę argumentów, które mają dla ciebie sens. Możesz również zaprojektować interfejs API, aby używał wielu bloków, jeśli lepiej to pasuje.

Oto dwa trywialne przykłady kleju segue. Pierwszy jest prosty i pokazuje jeden parametr przekazany dla danych wejściowych, drugi dla danych wyjściowych.

// 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];
}

Ten drugi przykład pokazuje przekazanie bloku zwrotnego dla drugiego argumentu. Lubię używać bloków, ponieważ utrzymuje odpowiednie szczegóły blisko siebie w źródle - źródle wyższego poziomu.

// 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];
    }];
}
WeakPointer
źródło
41

Przekazywanie danych z ViewController 2 (miejsce docelowe) do viewController 1 (Źródło) jest bardziej interesującą rzeczą. Zakładając, że używasz storyBoard, oto wszystkie sposoby, w jakie się dowiedziałem:

  • Delegat
  • Powiadomienie
  • Ustawienia domyślne użytkownika
  • Singel

Te zostały już tutaj omówione.

Odkryłem, że jest więcej sposobów:

-Używanie Block callback:

użyj go w prepareForSeguemetodzie w VC1

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

-Używanie scenariuszy Rozwiń (Wyjdź)

Zaimplementuj metodę z argumentem UIStoryboardSegue w VC 1, taką jak ta:

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

W StoryBoard zaczep przycisk „powrót” do zielonego przycisku wyjścia (Unwind) w vc. Teraz masz segment, który „wraca”, więc możesz użyć właściwości destinationViewController w preparForSegue VC2 i zmienić dowolną właściwość VC1, zanim ona wróci.

  • Inna opcja korzystania z scenariuszy Undwind (Wyjdź) - możesz użyć metody napisanej w VC1

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

    A w narzędziu PrepForSegue VC1 możesz zmienić dowolną właściwość, którą chcesz udostępnić.

W obu opcjach odwijania możesz ustawić właściwość tagu przycisku i zaznaczyć ją w preparForSegue.

Mam nadzieję, że dodałem coś do dyskusji.

:) Twoje zdrowie.

Jewgienij
źródło
40

Istnieje wiele metod udostępniania danych.

  1. Zawsze możesz udostępniać dane za pomocą NSUserDefaults. Ustaw wartość, którą chcesz udostępnić w odniesieniu do wybranego klucza i uzyskaj wartość z NSUserDefaultpowiązanego z tym kluczem w następnym kontrolerze widoku.

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
  2. Możesz po prostu utworzyć właściwość w viewcontrollerA. Utwórz obiekt viewcontrollerAw viewcontrollerBi przypisz żądaną wartość do tej właściwości.

  3. Możesz również utworzyć niestandardowych delegatów w tym celu.

Anubrata Santra
źródło
30
Typowym celem NSUserDefaults jest przechowywanie preferencji użytkownika, które utrzymują się między kolejnymi uruchomieniami aplikacji, więc wszystko tutaj przechowywane pozostanie tutaj, chyba że zostanie wyraźnie usunięte. To naprawdę zły pomysł, aby używać tego do przekazywania informacji między kontrolerami widoku (lub innymi obiektami) w aplikacji.
José González
30

Jeśli chcesz przekazać dane z jednego kontrolera do drugiego, wypróbuj ten kod

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];
 }
użytkownik2998756
źródło
29

To jest bardzo stara odpowiedź i jest to anty-wzór, proszę użyć delegatów. Nie używaj tego Podejścia !!

1. Utwórz instancję pierwszego kontrolera widoku w drugim kontrolerze widoku i wprowadź jego właściwość @property (nonatomic,assign).

2. Przypisz SecondviewControllerwystąpienie tego kontrolera widoku.

2. Po zakończeniu operacji zaznaczania skopiuj tablicę do pierwszego kontrolera widoku, gdy zwolnisz SecondView, FirstView będzie przechowywać dane macierzy.

Mam nadzieję że to pomoże.

kaar3k
źródło
2
Nie sądzę, że jest to właściwa droga, ponieważ tworzy bardzo nierówne połączenie między kontrolerami widoku. Nie bardzo trzymam się MVC.
Matt Price
1
Jeśli chcesz ściśle przestrzegać MVC, użyj NSNotificationCenter , można wywołać metodę z ViewControllerA do ViewControllerB, zaznacz to, że może to pomóc u
kaar3k
28

Długo szukałem tego rozwiązania, znalazłem je Atlast. Przede wszystkim zadeklaruj wszystkie obiekty w pliku SecondViewController.h, takie jak

@interface SecondViewController: UIviewController 
{
    NSMutableArray *myAray;
    CustomObject *object;
}

Teraz w pliku implementacji przydziel pamięć dla takich obiektów

- (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;
}

Teraz przydzieliłeś pamięć Array i obiekt. teraz możesz wypełnić tę pamięć, zanim ją wypchnieszViewController

Przejdź do SecondViewController.h i napisz dwie metody

-(void)setMyArray:(NSArray *)_myArray;
-(void)setMyObject:(CustomObject *)_myObject;

w pliku implementacji możesz zaimplementować funkcję

-(void)setMyArray:(NSArray *)_myArray
{
     [myArra addObjectsFromArray:_myArray];
}
-(void)setMyObject:(CustomObject *)_myObject
{
     [object setCustomObject:_myObject];
}

spodziewając się, że CustomObjectmusisz mieć z nim funkcję ustawiającą.

teraz twoja podstawowa praca jest skończona. przejdź do miejsca, w którym chcesz popchnąć SecondViewControlleri wykonaj następujące czynności

SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]] ;
[secondView setMyArray:ArrayToPass];
[secondView setMyObject:objectToPass];
[self.navigationController pushViewController:secondView animated:YES ];

Uważaj na błędy ortograficzne.

AsifHabib
źródło
24

Nie jest to sposób, aby to zrobić, powinieneś użyć delegatów, zakładam, że mamy dwa kontrolery widoku ViewController1 i ViewController2, a ta kontrola jest w pierwszym, a gdy jej stan się zmieni, chcesz coś zrobić w ViewController2, aby osiągnąć to we właściwy sposób, należy wykonać następujące czynności:

Dodaj nowy plik do projektu (Protokół Objective-C) -> Nowy, nadaj mu nazwę ViewController1Delegate lub cokolwiek chcesz i napisz je między dyrektywami @interface i @end

@optional

- (void)checkStateDidChange:(BOOL)checked;

Teraz przejdź do ViewController2.h i dodaj

#import "ViewController1Delegate.h"

następnie zmień jego definicję na

@interface ViewController2: UIViewController<ViewController1Delegate>

Teraz przejdź do ViewController2.m i wewnątrz implementacji dodaj:

- (void)checkStateDidChange:(BOOL)checked {
     if (checked) {
           // Do whatever you want here
           NSLog(@"Checked");
     }
     else {
           // Also do whatever you want here
           NSLog(@"Not checked");
     }
}

Teraz przejdź do ViewController1.h i dodaj następującą właściwość:

@property (weak, nonatomic) id<ViewController1Delegate> delegate; 

Teraz, jeśli tworzysz ViewController1 wewnątrz ViewController2 po jakimś zdarzeniu, powinieneś to zrobić w ten sposób, używając plików NIB:

ViewController1* controller = [[NSBundle mainBundle] loadNibNamed:@"ViewController1" owner:self options:nil][0];
controller.delegate = self;
[self presentViewController:controller animated:YES completion:nil];

Teraz wszystko jest gotowe, za każdym razem, gdy wykryjesz zdarzenie sprawdzania zmienione w ViewController1, wszystko co musisz zrobić, to:

[delegate checkStateDidChange:checked]; // You pass here YES or NO based on the check state of your control

Powiedz mi, czy jest coś, co nie jest jasne, jeśli nie zrozumiałem twojego pytania poprawnie.

Boda Taljo
źródło
23

Jeśli chcesz wysłać dane z jednego do drugiego viewController, oto sposób na to:

Powiedzmy, że mamy viewControllers: viewControllerA i viewControllerB

Teraz w widokuControllerB.h

@interface viewControllerB : UIViewController {

  NSString *string;
  NSArray *array;

}

- (id)initWithArray:(NSArray)a andString:(NSString)s;

In 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;

}

In 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];

}

W ten sposób możesz przekazywać dane z viewControllerA do viewControllerB bez ustawiania delegata. ;)

Aniruddh Joshi
źródło
1
Próbowałem użyć kodu ur w moim projekcie, ale nie jestem w stanie uzyskać wartości w viewcontrollerB. Czy możesz mi powiedzieć, na czym polega problem?
X-Coder
1
@Ajitthala Czy możesz wkleić kod w nowym pytaniu? Spróbuję rozwiązać Twój problem. :)
Aniruddh Joshi
1
czy źle jest nie używać metod init i po prostu zrobić coś takiego jak vcB.string = @ "asdf" z viewcontroller A?
khanh.tran.vinh
1
@ khanh.tran.vinh Zależy od tego, czy używasz ARC, czy nie.
Aniruddh Joshi
21

Wiem, że to pobity temat, ale dla tych, którzy chcą odpowiedzieć na to pytanie za pomocą skosu SWIFT i chcą goły przykład, oto moja metoda przekazywania danych, jeśli używasz segue do poruszania się.

Jest podobny do powyższego, ale bez przycisków, etykiet i tym podobnych. Po prostu przesyłanie danych z jednego widoku do drugiego.

Skonfiguruj Storyboard

Istnieją trzy części.

  1. Nadawca
  2. Segue
  3. Odbiornik

Jest to bardzo prosty układ widoku z odstępem między nimi.


Bardzo prosty układ widoku.  Uwaga: Brak kontrolera nawigacji


Oto konfiguracja dla nadawcy


Nadawca


Oto konfiguracja odbiornika.


Odbiornik


Wreszcie konfiguracja dla segue.


Identyfikator segmentu


Wyświetl kontrolery

Utrzymujemy to w prosty sposób, więc nie ma przycisków, a nie akcji, po prostu przenosimy dane od nadawcy do odbiornika, gdy aplikacja się ładuje, a następnie przesyłamy przesłaną wartość do konsoli.

Ta strona przyjmuje początkowo załadowaną wartość i przekazuje ją dalej.

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

    }

}

Ta strona po prostu wysyła wartość zmiennej do konsoli podczas ładowania. W tym momencie nasz ulubiony film powinien być w tej zmiennej.

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)")

    }   
}

W ten sposób możesz sobie z tym poradzić, jeśli chcesz użyć segue i nie masz stron pod kontrolerem nawigacyjnym.

Po uruchomieniu powinien automatycznie przełączyć się do widoku odbiorcy i przekazać wartość od nadawcy do odbiorcy, wyświetlając wartość w konsoli.

Ghost Busters to klasyczni ludzie.

Christopher Wade Cantley
źródło
19

W moim przypadku użyłem klasy singleton, która może działać jako obiekt globalny, umożliwiający dostęp do danych z niemal każdego miejsca w aplikacji. Pierwszą rzeczą jest zbudowanie klasy singleton. Proszę odnieść się do strony: „ Jak powinien wyglądać mój singleton Objective-C? ”. A co zrobiłem, aby obiekt był dostępny na całym świecie, to po prostu zaimportować go, appName_Prefix.pchaby zastosować instrukcję importu w każdej klasie. Aby uzyskać dostęp do tego obiektu i użyć, po prostu zaimplementowałem metodę klasy, aby zwrócić współużytkowaną instancję, która zawiera własne zmienne

petershine
źródło
To jest poprawna odpowiedź. Wystarczy użyć singletonu jako „modelu”. Pamiętaj, że jak mówi Caleb „model aplikacji może być tak prosty jak tablica ciągów” . Należy pamiętać, że wykonanie singletona w Swift jest naprawdę trywialne . (Tak proste, że nawet nie warto tutaj wspominać - wystarczy google.) Dla nowych programistów warto zrozumieć, że tworzenie singletonów było prawdziwym bólem w dupę . Jednak singletony są absolutnie kluczowe dla programowania na iOS - wszystko, co robi Apple, to singleton. Właśnie dlatego Apple w końcu sprawiło, że trtvial (w Swift) poprawnie tworzy singletony.
Fattie
1
Należy jednak pamiętać, że obecnie (2016+) „wszystko jest widokiem kontenera w iOS”. Każdą rzecz, którą robisz na ekranie, robisz mały widok pojemnika. Uzyskiwanie referencji „w górę i w dół” łańcuchów widoków kontenerów jest dość trywialne (chociaż Apple ułatwi to w przyszłości), i robisz to dla prawie każdego widoku kontenera. Tak więc, jeśli i tak to zrobiłeś - masz odpowiedź; nie potrzeba singletonu. Wprowadzenie do widoku kontenera ... stackoverflow.com/a/23403979/294884
Fattie
19

Szybki 5

Dobrze Matt Price Odpowiedź jest perfekcyjnie do przekazywania danych, lecz idę, aby go przerobić, w najnowszej wersji Swift ponieważ wierzę nowe programiści uważają, że jest rzucić wyzwanie ze względu na nową składnię i metody / ram, jak oryginalny post jest w Objective-C.

Istnieje wiele opcji przekazywania danych między kontrolerami widoku.

  1. Korzystanie z kontrolera nawigacyjnego Push
  2. Korzystanie z Segue
  3. Za pomocą delegata
  4. Korzystanie z obserwatora powiadomień
  5. Korzystanie z bloku

Zamierzam przepisać jego logikę w Swift przy użyciu najnowszego systemu iOS


Przekazywanie danych przez Push Controller Navigation : z ViewControllerA do ViewControllerB

Krok 1. Zadeklaruj zmienną w ViewControllerB

var isSomethingEnabled = false

Krok 2. Wydrukuj zmienną w metodzie ViewDidLoad ViewControllerB

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

Krok 3. W ViewController Przekaż dane podczas przepychania przez kontroler nawigacji

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

Oto pełny kod dla:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

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

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

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

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

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

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

Przekazywanie danych przez segment : Od ViewControllerA do ViewControllerB

Krok 1. Utwórz segment z ViewControllerA do ViewControllerB i podaj Identifier = showDetailSegue w Storyboard, jak pokazano poniżej

wprowadź opis zdjęcia tutaj

Krok 2. W ViewControllerB Zadeklaruj wykonalnego o nazwie IsomethingEnabled i wypisz jego wartość.

Krok 3. W ViewControllerA pass jest wartośćSomethingEnabled podczas przekazywania Segue

Oto pełny kod dla:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

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

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

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

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {
    var isSomethingEnabled = false

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

Przekazywanie danych przez delegata : z ViewControllerB do ViewControllerA

Krok 1. Zadeklaruj protokół ViewControllerBDelegate w pliku ViewControllerB, ale poza klasą

protocol ViewControllerBDelegate: NSObjectProtocol {

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

Krok 2. Zadeklaruj delegowanie zmiennej wystąpienia w ViewControllerB

var delegate: ViewControllerBDelegate?

Krok 3. Wyślij dane do delegowania wewnątrz metody viewDidLoad ViewControllerB

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

Krok 4. Potwierdź ViewControllerBDelegate w ViewControllerA

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

Krok 5. Potwierdź, że wdrożysz delegata w ViewControllerA

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

Krok 6. Zaimplementuj metodę delegowania do odbierania danych w ViewControllerA

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

Oto pełny kod dla:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController, ViewControllerBDelegate  {

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

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

    @IBAction func goToViewControllerForDelegate(_ sender: Any) {

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

ViewControllerB

import UIKit

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

class ViewControllerB: UIViewController {
    var delegate: ViewControllerBDelegate?

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

Przekazywanie danych przez Notification Observer : Od ViewControllerB do ViewControllerA

Krok 1. Ustaw i opublikuj dane w obserwatorze powiadomień w ViewControllerB

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

Krok 2. Dodaj obserwatora powiadomień w ViewControllerA

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

Krok 3. Odbierz wartość danych powiadomienia w ViewControllerA

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

Oto pełny kod dla:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController{

    override func viewDidLoad() {
        super.viewDidLoad()

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

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

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

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

Przekazywanie danych przez blok : z ViewControllerB do ViewControllerA

Krok 1. Zadeklaruj blok w ViewControllerB

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

Krok 2. Ustaw dane w bloku w ViewControllerB

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

Krok 3. Odbierz dane bloku w ViewControllerA

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

Oto pełny kod dla:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

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

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

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

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

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

    override func viewDidLoad() {
        super.viewDidLoad()

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

Kompletną przykładową aplikację można znaleźć na moim GitHub Daj mi znać, jeśli masz jakieś pytania na ten temat.

swiftBoy
źródło
18

Przekazywanie danych między FirstViewController do SecondViewController jak poniżej

Na przykład:

FirstViewController Wartość ciągu jako

StrFirstValue = @"first";

więc możemy przekazać tę wartość do drugiej klasy, używając poniższego kroku

1> Musimy utworzyć skrzynkę obiektu łańcuchowego w pliku SecondViewController.h

NSString *strValue;

2> Konieczność zadeklarowania właściwości jak poniżej poniżej deklaracji w pliku .h

@property (strong, nonatomic)  NSString *strSecondValue;

3> Potrzebujesz zsyntetyzować tę wartość w pliku FirstViewController.m poniżej deklaracji nagłówka

@synthesize strValue;

oraz w FirstViewController.h:

@property (strong, nonatomic)  NSString *strValue;

4> W FirstViewController, z której metody przechodzimy do drugiego widoku, napisz poniżej kod w tej metodzie.

SecondViewController *secondView= [[SecondViewController alloc]     
initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]];

[secondView setStrSecondValue:StrFirstValue];

[self.navigationController pushViewController:secondView animated:YES ];
Chris Alan
źródło
Po przejściu do SecondViewController, w jaki sposób przekazujesz dane z powrotem do FirstViewController?
bruno,
18

Obecnie wnoszę wkład w rozwiązanie tego problemu typu open source poprzez projekt o nazwie MCViewFactory, który można znaleźć tutaj:

https://github.com/YetiHQ/manticore-iosviewfactory

Chodzi o to, aby naśladować intentyczny paradygmat Androida, używając globalnej fabryki do zarządzania widokiem, na który patrzysz, i używając „intencji” do przełączania i przekazywania danych między widokami. Cała dokumentacja znajduje się na stronie github, ale oto kilka najważniejszych informacji:

Wszystkie widoki ustawiasz w plikach .XIB i rejestrujesz je w aplikacji delegowanej podczas inicjowania fabryki.

// Register activities

MCViewFactory *factory = [MCViewFactory sharedFactory];

// the following two lines are optional. 
[factory registerView:@"YourSectionViewController"]; 

Teraz w swoim VC, za każdym razem, gdy chcesz przejść do nowego VC i przekazywać dane, tworzysz nowe zamiary i dodajesz dane do słownika (saveInstanceState). Następnie ustaw aktualny cel fabryki:

MCIntent* intent = [MCIntent intentWithSectionName:@"YourSectionViewController"];
[intent setAnimationStyle:UIViewAnimationOptionTransitionFlipFromLeft];
[[intent savedInstanceState] setObject:@"someValue" forKey:@"yourKey"];
[[intent savedInstanceState] setObject:@"anotherValue" forKey:@"anotherKey"];
// ...
[[MCViewModel sharedModel] setCurrentSection:intent];

Wszystkie widoki zgodne z tym muszą być podklasami MCViewController, które pozwalają zastąpić nową metodę onResume:, umożliwiając dostęp do przekazanych danych.

-(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];
}

Mam nadzieję, że niektórzy z was uważają to rozwiązanie za przydatne / interesujące.


źródło
Czy wszystkie obiekty kontrolera mogłyby pobrać / ustawić wszystkie zarejestrowane słowniki w dowolnym zakresie? Głosuj za tym.
Itachi,
15

Utwórz właściwość na następny view controller .h i zdefiniuj getter i setter.

Dodaj to propertyw NextVC.h na nextVC

@property (strong, nonatomic) NSString *indexNumber;

Dodaj

@synthesize indexNumber; w NextVC.m

I ostatni

NextVC *vc=[[NextVC alloc]init];

vc.indexNumber=@"123";

[self.navigationController vc animated:YES];
Vivek Yadav
źródło
11

Istnieje wiele sposobów na zrobienie tego i ważne jest, aby wybrać właściwy. Prawdopodobnie jedna z największych decyzji architektonicznych dotyczy sposobu udostępniania lub uzyskiwania dostępu do kodu modelu w aplikacji.

Napisałem o tym post na blogu: Udostępnianie kodu modelu . Oto krótkie podsumowanie:

Udostępnione dane

Jednym z podejść jest współdzielenie wskaźników do obiektów modelu między kontrolerami widoku.

  • Brutalna iteracja na kontrolerach widoku (w Nawigacji lub Kontrolerze paska kart), aby ustawić dane
  • Ustaw dane w preparForSegue (jeśli storyboardy) lub init (jeśli programowe)

Ponieważ przygotowanie do segue jest najczęstszym przykładem jest tutaj:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    var next = segue.destinationViewController as NextViewController
    next.dataSource = dataSource
}

Niezależny dostęp

Innym podejściem jest obsługiwanie ekranu pełnego danych na raz i zamiast sprzęgania kontrolerów widoku ze sobą łączy każdy kontroler widoku z pojedynczym źródłem danych, do którego mogą uzyskać dostęp niezależnie.

Najczęstszym sposobem, w jaki to widziałem, jest instancja singletona . Jeśli DataAccesswięc byłby to twój obiekt singletonu, możesz wykonać następujące czynności w metodzie viewDidLoad UIViewController:

func viewDidLoad() {
    super.viewDidLoad()
    var data = dataAccess.requestData()
}

Istnieją dodatkowe narzędzia, które pomagają również przekazywać dane:

  • Obserwacja kluczowych wartości
  • NSNotification
  • Podstawowe dane
  • NSFetchedResultsController
  • Źródło danych

Podstawowe dane

Zaletą Core Data jest odwrotne relacje. Więc jeśli chcesz po prostu nadać obiektowi NotesViewController obiekt notatek, możesz to zrobić, ponieważ będzie on miał odwrotny związek z czymś innym, jak zeszyt. Jeśli potrzebujesz danych na notatniku w programie NotesViewController, możesz wrócić do wykresu obiektowego, wykonując następujące czynności:

let notebookName = note.notebook.name

Przeczytaj więcej na ten temat w moim blogu: Udostępnianie kodu modelu

Korey Hinton
źródło
10

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;
Mohsin Sabasara
źródło
10

Delegowanie to jedyne rozwiązanie, które wykonuje takie operacje, gdy używasz plików .xib, jednak wszystkie opisane powyżej odpowiedzi storyboarddotyczą plików .xib, których potrzebujesz do delegowania. to jedyne rozwiązanie, jakie możesz.

Innym rozwiązaniem jest jednorazowe zainicjowanie wzorca klasy singleton i użycie go w całej aplikacji.

użytkownik2786888
źródło
10

jeśli chcesz przekazać dane z ViewControlerOne do ViewControllerTwo wypróbuj te ..

wykonaj to w ViewControlerOne.h

 @property (nonatomic, strong) NSString *str1;

zrób to w ViewControllerTwo.h

 @property (nonatomic, strong) NSString *str2;

Syntezuj str2 w ViewControllerTwo.m

@interface ViewControllerTwo ()
@end
@implementation ViewControllerTwo
@synthesize str2;

wykonaj to w ViewControlerOne.m

 - (void)viewDidLoad
 {
   [super viewDidLoad];

  // Data or string you wants to pass in ViewControllerTwo..
  self.str1 = @"hello world";

 }

na przyciskach kliknij zdarzenie, zrób to ..

-(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];
}

wykonaj to w ViewControllerTwo.m

- (void)viewDidLoad
{
 [super viewDidLoad];
  NSLog(@"%@",str2);
}
krushnsinh
źródło
10

Możesz zapisać dane w aplikacji Delegat, aby uzyskać do nich dostęp na kontrolerach widoku w aplikacji. Wszystko, co musisz zrobić, to utworzyć udostępnioną instancję delegata aplikacji

AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

Na przykład

jeśli zadeklarujesz NSArray object *arrayXYZa, możesz uzyskać do niego dostęp w dowolnym kontrolerze widoku przezappDelegate.arrayXYZ

ak_tyagi
źródło
Jest to metoda wyboru dla hackathonu
Hai Feng Kao,
9

Jeśli chcesz wysłać dane z jednego do drugiego viewController, oto sposób na to:

Powiedzmy, że mamy viewControllers: ViewController i NewViewController.

w 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

w 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];

}

W NewViewController.h

#import <UIKit/UIKit.h>

@interface NewViewController : UITableViewController
{
    NSArray *arrayList;

    NSString *name,*age,*dob,*mobile;

}

@property(nonatomic, retain)NSArray *arrayList;

@end

W 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

W ten sposób możemy przekazać dane z jednego kontrolera widoku do innego kontrolera widoku ...

Sabs
źródło
8

Podoba mi się pomysł modelowania obiektów i próbnych obiektów opartych na NSProxy do zatwierdzania lub odrzucania danych, jeśli to, co wybierze użytkownik, można anulować.

Łatwo jest przekazywać dane, ponieważ jest to pojedynczy obiekt lub kilka obiektów, a jeśli powiedzmy, że kontroler UINavigationController, możesz zachować odniesienie do modelu w środku, a wszystkie kontrolery widoku wypychanego mogą uzyskać do niego dostęp bezpośrednio z kontrolera nawigacji.

Ben Sinclair
źródło
8

Widziałem wielu ludzi, którzy komplikują to przy użyciu tej didSelectRowAtPathmetody. Korzystam z podstawowych danych w moim przykładzie.

- (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 linie kodu w metodzie i gotowe.

App Dev Guy
źródło
6

Istnieje wiele odpowiedzi na te pytania, oferujących wiele różnych sposobów wykonywania komunikacji z kontrolerem widoku, które rzeczywiście działałyby, ale nigdzie nie wspomniano, które z nich najlepiej użyć, a które należy unikać.

W praktyce moim zdaniem zalecane jest tylko kilka rozwiązań:

  • Aby przekazać dane:
    • przesłonić prepare(for:sender:)metodę UIViewControllerkorzystania z storyboardu i segu
    • przekazywać dane przez inicjalizator lub właściwości podczas wykonywania przejścia kontrolera widoku przez trudny kod
  • Aby przekazać dane wstecz
    • zaktualizuj stan współdzielenia aplikacji (który możesz przekazać dalej między kontrolerami widoku za pomocą jednej z powyższych metod)
    • użyj delegacji
    • użyj odprężającego segue

Rozwiązania, których NIE zalecam używać:

  • Odwoływanie się bezpośrednio do poprzedniego kontrolera zamiast korzystania z delegowania
  • Udostępnianie danych za pośrednictwem singletonu
  • Przekazywanie danych przez delegata aplikacji
  • Udostępnianie danych za pomocą ustawień domyślnych użytkownika
  • Przekazywanie danych za pośrednictwem powiadomień

Rozwiązania te, choć działają krótkoterminowo, wprowadzają zbyt wiele zależności, które zniekształcają architekturę aplikacji i powodują więcej problemów później.

Dla zainteresowanych napisałem kilka artykułów, które bardziej szczegółowo omawiają te kwestie i podkreślają różne wady:

Matteo Manferdini
źródło