Chiusura di un controller di visualizzazione presentata


116

Ho una domanda teorica. Ora sto leggendo di Apple ViewController guida.

Scrissero:

Quando arriva il momento di chiudere un controller di visualizzazione presentato, l'approccio preferito è lasciare che il controller di visualizzazione che presenta lo ignori. In altre parole, quando possibile, lo stesso view controller che ha presentato il view controller dovrebbe assumersi anche la responsabilità di respingerlo. Sebbene esistano diverse tecniche per notificare al controller della vista che presenta la presentazione che il controller della vista presentato deve essere ignorato, la tecnica preferita è la delega.

Ma non riesco a spiegare, perché devo creare un protocollo nel VC presentato e aggiungere la variabile delegato, creare un metodo delegato nella presentazione di VC per ignorare il VC presentato, invece di una semplice chiamata nel metodo del controller di visualizzazione presentato

[self dismissViewControllerAnimated:NO completion:nil]?

Perché la prima scelta è migliore? Perché Apple lo consiglia?

Risposte:


122

Penso che Apple stia coprendosi un po 'le spalle qui per un pezzo di API potenzialmente insignificante.

  [self dismissViewControllerAnimated:NO completion:nil]

In realtà è un po 'un violino. Sebbene tu possa - legittimamente - chiamarlo sul controller di visualizzazione presentato, tutto ciò che fa è inoltrare il messaggio al controller di visualizzazione che presenta. Se vuoi fare qualcosa al di là del semplice licenziamento del VC, dovrai saperlo e devi trattarlo più o meno allo stesso modo di un metodo delegato, poiché è più o meno quello che è, un integrato un po 'inflessibile metodo delegato.

Forse si sono imbattuti in un sacco di codice errato da parte di persone che non capiscono davvero come questo sia messo insieme, da qui la loro cautela.

Ma ovviamente, se tutto ciò che devi fare è ignorare la cosa, vai avanti.

Il mio approccio è un compromesso, almeno mi ricorda cosa sta succedendo:

  [[self presentingViewController] dismissViewControllerAnimated:NO completion:nil]

[Swift]

  self.presentingViewController?.dismiss(animated: false, completion:nil)

26
Va notato che l'uso presentingViewControllerè per lo più inutile in quanto si riferirà UINavigationControllerall'if selfè incorporato in uno. In tal caso, non sarai in grado di ottenerlo presentingViewControlleraffatto. Eppure, [self dismissViewControllerAnimated:completion]funziona ancora in quel caso. Il mio suggerimento sarebbe di continuare a usarlo fino a quando Apple non lo risolverà.
memmons

4
Mi piace che questa risposta sia ancora del tutto rilevante 3 anni dopo.
user1021430

1
Un'altra cosa da considerare è che un controller di visualizzazione non sa come è stato visualizzato. Potrebbe essere stato presentato, inserito in un controller di navigazione, parte di un controller della barra delle schede, ecc. L'utilizzo del delegato consente al controller di visualizzazione di "presentazione" di "ignorare" il controller di visualizzazione utilizzando l'inverso del metodo utilizzato per presentarlo.
David Smith il

51

Aggiornato per Swift 3

Sono venuto qui solo volendo chiudere il controller di visualizzazione corrente (presentato). Sto dando questa risposta a chiunque venga qui con lo stesso scopo.

Controller di navigazione

Se utilizzi un controller di navigazione, è abbastanza semplice.

Torna al controller di visualizzazione precedente:

// Swift
self.navigationController?.popViewController(animated: true)

// Objective-C
[self.navigationController popViewControllerAnimated:YES];

Torna al controller di visualizzazione principale:

// Swift
self.navigationController?.popToRootViewController(animated: true)

// Objective-C
[self.navigationController popToRootViewControllerAnimated:YES];

(Grazie a questa risposta per Objective-C.)

Controller di visualizzazione modale

Quando un controller di visualizzazione viene presentato in modo modale, è possibile ignorarlo (dal secondo controller di visualizzazione) chiamando

// Swift
self.dismiss(animated: true, completion: nil)

// Objective-C
[self dismissViewControllerAnimated:YES completion:nil];

La documentazione dice,

Il controller della visualizzazione che presenta è responsabile della chiusura del controller della visualizzazione presentato. Se chiami questo metodo sul controller della visualizzazione presentato stesso, UIKit chiede al controller della visualizzazione della presentazione di gestire il licenziamento.

Quindi funziona per il controller della vista presentato per chiamarlo su se stesso. Ecco un esempio completo.

I delegati

La domanda del PO riguardava la complessità dell'utilizzo di delegati per respingere una vista.

A questo punto non ho avuto bisogno di utilizzare i delegati poiché di solito ho un controller di navigazione o controller di visualizzazione modali, ma se dovrò utilizzare il pattern delegato in futuro, aggiungerò un aggiornamento.


50

Questo è per la riutilizzabilità del controller di visualizzazione.

Al tuo controller di visualizzazione non dovrebbe importare se viene presentato come modale, inserito in un controller di navigazione o altro. Se il tuo controller di visualizzazione si chiude da solo, stai assumendo che venga presentato in modo modale. Non sarai in grado di spingere quel controller di visualizzazione su un controller di navigazione.

Implementando un protocollo, consenti al controller della visualizzazione genitore di decidere come deve essere presentato / inviato e ignorato / espulso.



6

Nella mia esperienza, è utile quando è necessario chiuderlo da qualsiasi ViewController che desideri ed eseguire attività diverse per ogni viewcontroller che lo elimina. Qualsiasi viewController che adotta il protocollo può ignorare la visualizzazione a modo suo. (ipad vs iphone, o passaggio di dati diversi durante la chiusura da viste diverse, chiamata a metodi diversi durante la chiusura, ecc.)

Modificare:

Quindi, per chiarire, se tutto ciò che vuoi fare è chiudere la visualizzazione, non vedo la necessità di impostare il protocollo delegato. Se è necessario eseguire operazioni diverse dopo averlo ignorato da diversi controller di visualizzazione di presentazione, sarebbe il modo migliore per utilizzare il delegato.


ma se non ho bisogno di "passare dati diversi durante il licenziamento da viste diverse, chiamare metodi diversi durante il licenziamento, ecc.", posso fare una piccola chiamata nel metodo del controller di visualizzazione presentato - [self dismissViewControllerAnimated: NO completamento: nil]?
Nikitahils

Lasciare che il presentatore ignori la vista presentata, rende evidente che il presentatore è effettivamente pronto e gestisce il ritorno in primo piano: la sequenza di esecuzione è facile da seguire e la responsabilità di qualsiasi aggiornamento dell'interfaccia utente è implicitamente chiara.
Johan

2

Citazione dalla Guida alla programmazione del controller di visualizzazione , "Come i controller di visualizzazione presentano altri controller di visualizzazione".

Ogni controller di visualizzazione in una catena di controller di visualizzazione presentati ha puntatori agli altri oggetti che lo circondano nella catena. In altre parole, un controller di visualizzazione presentato che presenta un altro controller di visualizzazione ha oggetti validi sia nelle proprietà presentationViewController che presentateViewController. È possibile utilizzare queste relazioni per tracciare la catena di controller di visualizzazione secondo necessità. Ad esempio, se l'utente annulla l'operazione corrente, è possibile rimuovere tutti gli oggetti nella catena chiudendo il primo controller di visualizzazione presentato. La chiusura di un controller di visualizzazione elimina non solo tale controller di visualizzazione, ma anche tutti i controller di visualizzazione che ha presentato.

Quindi da un lato crea un bel design equilibrato, un buon disaccoppiamento, ecc ... Ma dall'altro è molto pratico, perché puoi tornare rapidamente a un certo punto della navigazione.

Anche se, personalmente preferirei utilizzare le sequenze di svolgimento piuttosto che provare a percorrere all'indietro l' albero dei controller di visualizzazione che presenta , che è ciò di cui parla Apple in questo capitolo da cui proviene la citazione.


2

Un punto è che questo è un buon approccio alla codifica. Soddisfa molti OOPprincipi, ad es. SRP, separazione delle preoccupazioni ecc.

Quindi, il controller della visualizzazione che presenta la visualizzazione dovrebbe essere quello che la ignora.

Ad esempio, una società immobiliare che dà una casa in affitto dovrebbe essere l'autorità per riprenderla.


2

Swift 3.0 // Chiudi il controller di visualizzazione in swift

self.navigationController?.popViewController(animated: true)
dismiss(animated: true, completion: nil)

1

Oltre alla risposta di Michael Enriquez, posso pensare a un altro motivo per cui questo potrebbe essere un buon modo per proteggerti da uno stato indeterminato:

Diciamo che ViewControllerA presenta ViewControllerB in modo modale. Tuttavia, poiché potresti non aver scritto il codice per ViewControllerA, non sei a conoscenza del ciclo di vita di ViewControllerA. Potrebbe chiudere 5 secondi (diciamo) dopo aver presentato il controller della vista, ViewControllerB.

In questo caso, se dismissViewControllerusassi semplicemente da ViewControllerB per chiudersi, finiresti in uno stato indefinito - forse non un crash o uno schermo nero ma uno stato indefinito dal tuo punto di vista.

Se, invece, stavi usando il pattern delegate, saresti a conoscenza dello stato di ViewControllerB e potrai programmare un caso come quello che ho descritto.


1

veloce

let rootViewController:UIViewController = (UIApplication.shared.keyWindow?.rootViewController)!

        if (rootViewController.presentedViewController != nil) {
            rootViewController.dismiss(animated: true, completion: {
                //completion block.
            })
        }

0

Se si utilizza la visualizzazione uso modale, chiudere.

[self dismissViewControllerAnimated:NO completion:nil];

Come risponde questo alla domanda: "Perché la prima scelta è migliore? Perché Apple la consiglia?"
jww

0

Questo è un sacco di sciocchezze. La delega va bene quando è necessaria, ma se rende il codice più complesso - e lo fa - allora ci deve essere una ragione per questo.

Sono sicuro che Apple abbia le sue ragioni. Ma è più chiaro e conciso semplicemente che il VC presentato esegua il licenziamento a meno che non ci sia una vera ragione per fare diversamente e nessuno qui a oggi ne ha presentato uno che io possa vedere.

I protocolli sono eccellenti quando sono necessari, ma la progettazione orientata agli oggetti non ha mai riguardato i moduli che comunicano inutilmente tra loro.

Tom Love (co-sviluppatore di Objective C) una volta ha commentato che Objective C era "elegante", "piccolo", "nitido" e "ben definito" (se confrontato con C ++). Facile per lui da dire. La delega è una caratteristica utile che sembra essere stata troppo usata "solo perché", e anche se mi piace lavorare nella lingua, temo l'idea di sentirmi costretto a usare una sintassi non necessaria per rendere le cose più complesse di quanto dovrebbero essere.


All'inizio potrebbe farti risparmiare un po 'di codice, ma il tuo approccio ti causerà molti mal di testa man mano che la tua base di codice cresce. Dovresti comprendere i principi orientati agli oggetti come la separazione delle preoccupazioni, altrimenti potresti anche codificare l'intera applicazione in un unico grande file.
Werner Altewischer

-2

Puoi chiudere la finestra di super visualizzazione

self.view.superview?.window?.close()

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.