2014-12-13 8 views
6

Próbuję nauczyć się Swift i staram się opracować słynną aplikację do obsługi notatek.Czy prepareForSegue właściwy sposób przekazywania wartości między kontrolerami widoku

Istnieje tablica powiązana z widokiem tableview i innym widokiem do dodawania notatek. W drugim widoku zdarzenia zdarzenia textfieldbreak wyzwala segue i wraca do tableview.

Chciałem się dowiedzieć, czy to jest właściwa droga. Ponieważ w ten sposób manipuluję zmienną w innym kontrolerze widoku. Nie jestem mistrzem MVC, ale czułem, że jest źle. Oto mój fragment kodu:

func textFieldShouldReturn(textField: UITextField) -> Bool { 

    self.performSegueWithIdentifier("backSegue", sender: self) 
    return true 
} 

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 
    if(segue.identifier == "backSegue"){ 
     let navController = segue.destinationViewController as UINavigationController; 
     let myController = navController.topViewController as NotesTableViewController; 
     if(self.ourTextField?.text != nil || self.ourTextField?.text != ""){ 
      myController.notes.append(self.ourTextField?.text ?? ""); 
     } 

    } 
} 

Dziękuję.

+0

Dlaczego * Czujesz się źle? To jest właściwy sposób. Kontrolery są używane do zarządzania danymi i widokami tych danych, więc gdy pchnięcie segue (lub pop) jest kolejną kontrolerą widoku, rozsądne jest przekazanie pewnych danych pomiędzy źródłem a miejscem docelowym. –

+0

Jest to zdecydowanie bardzo popularny sposób przekazywania danych między kontrolerami widoku i nie powinieneś się martwić o jego używanie. Mnóstwo [szanowanych programistów] (http://oleb.net/blog/2012/02/passing-data-between-view-controllers/) to robią. Nie jest to jednak jedyna prawdziwa droga i istnieją inne opcje. O ile mi wiadomo, Swift nie przynosi nic specjalnego do stołu w tym zakresie, więc powinieneś sprawdzić [istniejące pytanie Celu C] (http://stackoverflow.com/questions/5210535/passing-data-between -view-controllers). –

Odpowiedz

7

Twoje pytanie nie dotyczy tak naprawdę prepareForSegue, ale relacji między kontrolerami widoku. Powód, dla którego twój projekt "czuje się źle", jest taki. Problem polega na tym, że Twój kontroler widoku zapisu notatek wie zbyt wiele o kontrolerze widoku, który go używa ponieważ bezpośrednio manipuluje zmienną z kontrolera widoków wywołujących. Aby bezpośrednio manipulować zmienną, musi znać klasę dzwoniącego.

Dlaczego to jest problem? Sprawia, że ​​Twój kontroler widoku notatek jest mniej przydatny do ponownego użycia. Jeśli poprawnie napiszesz kontroler widoku zapisu notatek, możesz go ponownie użyć w innych aplikacjach. Aby było to możliwe do ponownego użycia, musisz odłączyć kontroler widoku zapisu notatki od wywołującego - nie może on wiedzieć, kto dokładnie to nazywa.

Pojawia się pytanie, w jaki sposób przekazać dane do dzwoniącego, jeśli nie wiem, kto do mnie dzwonił? Odpowiedź brzmi: delegacja.

Delegacja działa tak:

  1. utworzyć protokół która opisuje metodę lub metody, które implementor tego protokołu będzie wdrożyć. W twoim przypadku możesz użyć protokołu takiego jak NoteWriterDelegate, który implementuje metodę takeNote(note: String).

    protocol NoteWriterDelegate { 
        func takeNote(note: String) 
    } 
    

    Definicja tego w pliku wraz z kontrolerem widoku zapisu notatki.

  2. Twoja notatka pisarz będzie miał opcjonalny wskaźnik do delegata:

    weak var delegate: NoteWriterDelegate? 
    
  3. Musisz zadeklarować swój pierwszy kontroler widoku jako NoteWriterDelegate:

    class ViewController: UITableViewController, NoteWriterDelegate 
    
  4. a następnie wdrożyć wymagane metoda z pierwszego kontrolera widoku:

    func takeNote(note: String) { 
        notes.append(note) 
    } 
    
  5. Po wywołaniu prepareForSegue w ramach przygotowań do przejścia do notatkę pisząc widok kontrolera, należy zdać sobie jako delegata:

    destinationViewController.delegate = self 
    
  6. w widoku kontrolera uwaga pisania, kiedy masz wiadomości do przekazania z powrotem do rozmówcy, zadzwonić takeNote na delegata:

    delegate?.takeNote(self.ourTextField?.text ?? "") 
    

Robiąc to w ten sposób, Twoja uwaga pisarz wie, że mówi do NoteWriterDelegate. Jeśli chcesz ponownie użyć tego w przyszłości, po prostu upuść swoją klasę edytora notatek do innego projektu, zaimplementuj delegata i działa bez konieczności dotykania kodu z klasy zapisującej notatki.

2

Zalecam przekazywanie danych przez prepareForSegue w większości przypadków. Jest prosty w konfiguracji i łatwy do zrozumienia.

Zalecam jednak, aby nigdy nie aktualizować elementów interfejsu użytkownika (etykiet, pól tekstowych itp.) Bezpośrednio w widoku docelowym. Moim zdaniem jest to złe sprzężenie, które stwarza wiele problemów.

Zamiast tego utwórz właściwość lub właściwości na docelowym kontrolerze widoku, które wywoływacz może ustawić w pliku prepareForSegue, aby przekazać do niego dane. Powinny to być właściwości specjalnego przeznaczenia używane wyłącznie do przekazywania danych. Kontroler widoku docelowego jest następnie odpowiedzialny za wykorzystanie danych w tych właściwościach do aktualizacji swojego interfejsu lub stanu wewnętrznego.

Delegacja jest prawidłowym podejściem, ale w większości sytuacji uważam, że jest to przesada. Wymaga większej konfiguracji i jest bardziej abstrakcyjna. Ta abstrakcja nie jest potrzebna w wielu relacjach kontrolerów widoku. Jeśli odkryjesz, że musisz ponownie użyć kontrolera widoku, zawsze możesz refaktoryzować, aby użyć delegacji później.

2

Nie sądzę, że prepareSegue jest idealnym sposobem przekazywania danych między kontrolerami widoku ... przynajmniej nie bezpośrednio.

Podzielam twoje obawy dotyczące używania prepareForSegue do przekazywania wartości między kontrolerami widoku. Kontroler widoku źródłowego nie powinien wiedzieć nic o docelowym kontrolerze widoku (i na odwrót, jeśli o to chodzi). Najlepiej byłoby, gdyby kontrolery widoku w widoku były oddzielnymi wyspami, bez wzajemnej widoczności.

Aby rozwiązać problem sprzężenia, które wydają się zachęcać storyboardy, często używam formy mediator pattern do przekazywania danych między kontrolerami widoku. Oto całkiem niezły post na blogu o tym, jak zaimplementować wersję tego wzorca wokół scenorysów: http://coding.tabasoft.it/ios/mediator-pattern-in-swift/. Jak zawsze, ten wzór może nie pasować do wszystkich sytuacji, ale uważam, że było to dobre rozwiązanie w wielu moich poprzednich projektach.

Zasadniczo, w jaki sposób wzorzec mediatora działałby zgodnie z paradygmatem storyboardu, jest to, że w metodzie prepareForSegue każdego widoku kontroler obiekt przejścia jest przekazywany do obiektu mediatora. Kontroler widoku nie dba o to, co jest w środku lub dokąd nawigacja idzie dalej; po prostu wie, że niedługo nie będzie widać. Mediator, który właśnie przekazał obiekt przesuwania (zawierający kontrolery widoku źródłowego i docelowego), jest następnie odpowiedzialny za przekazywanie danych między kontrolerami widoku źródłowego i docelowego.

Korzystając z tego wzoru, każdy kontroler widoku jest błogo nieświadomy istnienia drugiego. Z drugiej strony, klasa mediatora musi wiedzieć o relacjach między kontrolerami widoku (i interfejsami kontrolerów widoku) w ścieżce nawigacji. Oczywiście, jeśli nawigacja ulegnie zmianie lub same kontrolery widoku się zmienią, klasa mediatora będzie musiała się dostosować. Każdy kontroler widoku nie musi jednak być zależny od siebie nawzajem i dlatego nie musi być aktualizowany w celu uwzględnienia zmian w ścieżce nawigacji lub zmian w innych kontrolerach widoku wzdłuż tej ścieżki nawigacji.

0

To nie jest "właściwa droga", ale jest to właściwa droga. Zwłaszcza w zastosowaniach do scenorysów.

Oto alternatywny sposób przekazywania wartości i wywoływania widoku.

var myNewVC = NewViewController() 
myNewVC.data = self 
navigationController?.presentViewController(myNewVC, animated: true, completion: nil) 
Powiązane problemy