iOS
Przekazywanie danych między kontrolerami widoku
Szukaj…
Korzystanie z segmentów (przekazywanie danych do przodu)
Aby przekazać dane z bieżącego kontrolera widoku do następnego nowego kontrolera widoku (nie poprzedniego kontrolera widoku) przy użyciu segu, najpierw utwórz segregację z identyfikatorem w odpowiedniej scenorysie. Zastąp metodę prepareForSegue
bieżącego kontrolera prepareForSegue
. Wewnątrz metody sprawdź segment utworzony właśnie przez jego identyfikator. Rzuć kontroler widoku docelowego i przekaż mu dane, ustawiając właściwości na kontrolerze widoku w dół.
Ustawienie identyfikatora dla segue:
Segmenty można wykonywać programowo lub przy użyciu zdarzenia akcji przycisku ustawionego w serii ujęć za pomocą kombinacji klawiszy Ctrl + Przeciągnij do kontrolera widoku docelowego. W razie potrzeby możesz wywoływać segue programowo, używając identyfikatora segue w kontrolerze widoku:
Cel C
- (void)showDetail {
[self performSegueWithIdentifier:@"showDetailingSegue" sender:self];
}
Szybki
func showDetail() {
self.performSegue(withIdentifier: "showDetailingSegue", sender: self)
}
Można skonfigurować ładunek segue w przesłoniętej wersji metody prepareForSegue
. Można ustawić wymagane właściwości przed załadowaniem docelowego kontrolera widoku.
Cel C
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:@"showDetailingSegue"]){
DetailViewController *controller = (DetailViewController *)segue.destinationViewController;
controller.isDetailingEnabled = YES;
}
}
Szybki
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetailingSegue" {
let controller = segue.destinationViewController as! DetailViewController
controller.isDetailingEnabled = true
}
}
DetailViewController
to nazwa drugiego kontrolera widoku, a isDetailingEnabled
to zmienna publiczna w tym kontrolerze widoku.
Aby rozwinąć ten wzorzec, można traktować metodę publiczną na DetailViewController
jako pseudoinicjalizator, aby pomóc w inicjalizacji wymaganych zmiennych. Spowoduje to samodzielne dokumentowanie zmiennych, które należy ustawić w DetailViewController
bez konieczności czytania jego kodu źródłowego. Jest to również przydatne miejsce do ustawienia domyślnych.
Cel C
- (void)initVC:(BOOL *)isDetailingEnabled {
self.isDetailingEnabled = isDetailingEnabled
}
Szybki
func initVC(isDetailingEnabled: Bool) {
self.isDetailingEnabled = isDetailingEnabled
}
Korzystanie ze wzoru delegowania (przekazywanie danych z powrotem)
Aby przekazać dane z bieżącego kontrolera widoku z powrotem do poprzedniego kontrolera widoku, możesz użyć wzorca delegowania.
W tym przykładzie założono, że utworzono segue w Konstruktorze interfejsów i ustawiono identyfikator segue na showSecondViewController
. Punkty sprzedaży i działania muszą być również podłączone do nazw w poniższym kodzie.
Kontroler pierwszego widoku
Kod kontrolera First View to
Szybki
class FirstViewController: UIViewController, DataEnteredDelegate {
@IBOutlet weak var label: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecondViewController", let secondViewController = segue.destinationViewController as? SecondViewController {
secondViewController.delegate = self
}
}
// required method of our custom DataEnteredDelegate protocol
func userDidEnterInformation(info: String) {
label.text = info
navigationController?.popViewControllerAnimated(true)
}
}
Cel C
@interface FirstViewController : UIViewController <DataEnteredDelegate>
@property (weak, nonatomic) IBOutlet UILabel *label;
@end
@implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
SecondViewController *secondViewController = segue.destinationViewController;
secondViewController.delegate = self;
}
-(void)userDidEnterInformation:(NSString *)info {
_label.text = info
[self.navigationController popViewControllerAnimated:YES];
}
@end
Zwróć uwagę na użycie naszego niestandardowego protokołu DataEnteredDelegate
.
Drugi widok kontrolera i protokołu
Kod drugiego kontrolera widoku to
Szybki
// 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?
@IBOutlet weak var textField: UITextField!
@IBAction func sendTextBackButton(sender: AnyObject) {
// call this method on whichever class implements our delegate protocol (the first view controller)
delegate?.userDidEnterInformation(textField.text ?? "")
}
}
Cel C
@protocol DataEnteredDelegate <NSObject>
-(void)userDidEnterInformation:(NSString *)info;
@end
@interface SecondViewController : UIViewController
@property (nonatomic) id <DataEnteredDelegate> delegate;
@property (weak, nonatomic) IBOutlet UITextField *textField;
@end
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (IBAction) sendTextBackButton:(id)sender{
[_delegate userDidEnterInformation:textField.text];
}
@end
Zauważ, że protocol
znajduje się poza klasą View Controller.
Przekazywanie danych do tyłu za pomocą cofania do segregacji
W przeciwieństwie do segue, który pozwala przekazywać dane „do przodu” z bieżącego kontrolera widoku do docelowego kontrolera widoku:
(VC1) -> (VC2)
Korzystając z funkcji „odprężenia”, można zrobić odwrotnie, przekazać dane z docelowego lub bieżącego kontrolera widoku do jego kontrolera prezentacji:
(VC1) <- (VC2)
UWAGA : Zwróć uwagę, że użycie odprężania pozwala najpierw przekazać dane, a następnie bieżący kontroler widoku (VC2) zostanie zwolniony.
Oto jak to zrobić:
Najpierw musisz dodać następującą deklarację do kontrolera widoku prezentującego (VC1), który jest kontrolerem widoku, do którego chcemy przekazać dane:
@IBAction func unwindToPresentingViewController(segue:UIStoryboardSegue)
Ważne jest, aby użyć prefiksu unwind
, „informuje” Xcode, że jest to metoda odwijania, dająca możliwość użycia jej również w serii ujęć.
Następnie musisz zaimplementować metodę, wygląda ona prawie tak samo jak rzeczywisty segue:
@IBAction func unwindToPresentingViewController(segue:UIStoryboardSegue)
{
if segue.identifier == "YourCustomIdentifer"
{
if let VC2 = segue.sourceViewController as? VC2
{
// Your custom code in here to access VC2 class member
}
Teraz masz 2 opcje wywoływania połączeń relaksacyjnych:
- Możesz „na
self.performSegueWithIdentifier("YourCustomIdentifier", sender: self)
kodować” wywoływać:self.performSegueWithIdentifier("YourCustomIdentifier", sender: self)
który zrobi dla ciebie odpoczynek, ilekroć wykonaszperformSegueWithIdentifier
. - Możesz połączyć metodę odwijania za pomocą
storyboard
z obiektem „Wyjdź”: ctrl + przeciągnij przycisk, który chcesz wywołać metodę odwijania, do obiektu „Wyjdź”:
Zwolnij, a będziesz mieć możliwość wyboru niestandardowej metody odwijania:
Przekazywanie danych za pomocą zamknięć (przekazywanie danych z powrotem)
Zamiast używać wzorca delegowania , który dzieli implementację na różne części klasy UIViewController
, można nawet używać closures
do przekazywania danych w przód iw tył. Zakładając, że korzystasz z UIStoryboardSegue
, w metodzie prepareForSegue
możesz łatwo skonfigurować nowy kontroler w jednym kroku
final class DestinationViewController: UIViewController {
var onCompletion: ((success: Bool) -> ())?
@IBAction func someButtonTapped(sender: AnyObject?) {
onCompletion?(success: true)
}
}
final class MyViewController: UIViewController {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
guard let destinationController = segue.destinationViewController as? DestinationViewController else { return }
destinationController.onCompletion = { success in
// this will be executed when `someButtonTapped(_:)` will be called
print(success)
}
}
}
Jest to przykład użycia i lepiej go używać w Swift, składnia bloku Objective-C nie jest tak łatwa, aby kod był bardziej czytelny
Wykorzystanie zamknięcia (bloku) oddzwaniania przekazującego dane z powrotem
ten temat jest klasycznym zagadnieniem w rozwoju iOS, a jego rozwiązanie jest różne, jak pokazano już w innym przykładzie. W tym przykładzie pokażę inny typowy codzienny sposób użycia: przekazywanie danych przy użyciu closure
poprzez dostosowanie przykładu delegate pattern
na tej stronie do closure
wywołania zwrotnego!
jedna metoda jest lepsza niż delegate pattern
- zamiast rozdzielić kod konfiguracyjny w dwa różne miejsca (spójrz na przykład delegowania na tej stronie, prepareForSegue
, userDidEnterInformation
), zamiast gromadzić je razem (tylko w prepareForSegue
, pokażę to)
Zacznij od drugiego kontrolera widoku
musimy dowiedzieć się, jak korzystać z wywołania zwrotnego, a następnie możemy to napisać, dlatego zaczynamy od drugiego kontrolera widoku, ponieważ tam właśnie używamy wywołania zwrotnego: kiedy otrzymaliśmy nowe wprowadzanie tekstu, wywołujemy nasze wywołanie zwrotne, używając parametru wywołania zwrotnego jako medium aby przekazać dane z powrotem do pierwszego ViewController, zauważ, że powiedziałem, używając parametru wywołania zwrotnego, jest to bardzo ważne, nowicjusze (tak jak ja) zawsze przeoczają to i nie wiedzą, od czego zacząć poprawnie pisać zamknięcie wywołania zwrotnego
więc w tym przypadku wiemy, że nasze wywołanie zwrotne przyjmuje tylko jeden parametr: tekst, a jego typ to String
, zadeklarujmy go i ustawmy jako właściwość, ponieważ musimy wypełnić go z naszego pierwszego kontrolera widoku
Po prostu komentuję całą część delegate
i zachowuję ją do porównania
class SecondViewController: UIViewController {
//weak var delegate: DataEnteredDelegate? = nil
var callback: ((String?)->())?
@IBOutlet weak var textField: UITextField!
@IBAction func sendTextBackButton(sender: AnyObject) {
//delegate?.userDidEnterInformation(textField.text!)
callback?(input.text)
self.navigationController?.popViewControllerAnimated(true)
}
}
Zakończ pierwszy widok kontrolera
wszystko, co musisz zrobić, to przekazać zamknięcie oddzwonienia, a my skończymy, zamknięcie wykona dla nas przyszłą pracę, ponieważ już skonfigurowaliśmy to w drugim kontrolerze widoku
zobacz, jak skraca nasz kod w porównaniu do delegate pattern
//no more DataEnteredDelegate
class FirstViewController: UIViewController {
@IBOutlet weak var label: UILabel!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showSecondViewController" {
let secondViewController = segue.destinationViewController as! SecondViewController
//secondViewController.delegate = self
secondViewController.callback = { text in self.label.text = text }
}
}
// required method of our custom DataEnteredDelegate protocol
//func userDidEnterInformation(info: String) {
// label.text = info
//}
}
i na koniec, być może ktoś z was będzie zdezorientowany tym, że przekazujemy dane (w tym przypadku zamknięcie) tylko w jeden sposób, od pierwszego kontrolera widoku do drugiego, bez bezpośredniego powrotu z drugiego kontrolera widoku, jak możemy uznać to za narzędzie komunikacji? może naprawdę powinieneś go uruchomić i sam to udowodnić, powiem tylko, że to parametr , to parametr zamknięcia wywołania zwrotnego, który przekazuje dane z powrotem!
Przypisując właściwość (Przekazywanie danych do przodu)
Możesz przekazać dane bezpośrednio, przypisując właściwość następnego kontrolera widoku przed jego wypchnięciem lub zaprezentowaniem.
class FirstViewController: UIViewController {
func openSecondViewController() {
// Here we initialize SecondViewController and set the id property to 492
let secondViewController = SecondViewController()
secondViewController.id = 492
// Once it was assign we now push or present the view controller
present(secondViewController, animated: true, completion: nil)
}
}
class SecondViewController: UIViewController {
var id: Int?
override func viewDidLoad() {
super.viewDidLoad()
// Here we unwrapped the id and will get the data from the previous view controller.
if let id = id {
print("Id was set: \(id)")
}
}
}