iOS
Passaggio dei dati tra i controller di visualizzazione
Ricerca…
Utilizzo di Segues (passaggio dei dati in avanti)
Per passare i dati dal controller della vista corrente al nuovo controller della vista successivo (non un precedente controller della vista) usando segues, prima creare un seguito con un identificatore nello storyboard pertinente. Sostituisci il metodo prepareForSegue
del tuo attuale controller di prepareForSegue
. All'interno del metodo controlla il seguito appena creato dal suo identificatore. Trasmetti il controller di visualizzazione di destinazione e passa i dati ad esso impostando le proprietà sul controller di visualizzazione downcast.
Impostazione di un identificatore per un seguito:
Le sequenze possono essere eseguite in modo programmatico o utilizzando l'evento dell'azione del pulsante impostato nello storyboard da ctrl + trascinamento al controller della vista di destinazione. È possibile chiamare un passaggio in modo programmatico, quando necessario, utilizzando l'identificatore dei passaggi nel controllore della vista:
Objective-C
- (void)showDetail {
[self performSegueWithIdentifier:@"showDetailingSegue" sender:self];
}
veloce
func showDetail() {
self.performSegue(withIdentifier: "showDetailingSegue", sender: self)
}
È possibile configurare il payload dei passaggi nella versione sovrascritta del metodo prepareForSegue
. È possibile impostare le proprietà richieste prima che il controller della vista di destinazione sia caricato.
Objective-C
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:@"showDetailingSegue"]){
DetailViewController *controller = (DetailViewController *)segue.destinationViewController;
controller.isDetailingEnabled = YES;
}
}
veloce
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetailingSegue" {
let controller = segue.destinationViewController as! DetailViewController
controller.isDetailingEnabled = true
}
}
DetailViewController
è il nome del secondo controller di visualizzazione e isDetailingEnabled
è una variabile pubblica in tale controller di visualizzazione.
Per espandere questo modello, puoi trattare un metodo pubblico su DetailViewController
come pseudo inizializzatore, per aiutare a inizializzare le variabili richieste. Questo documenterà automaticamente le variabili che devono essere impostate su DetailViewController
senza dover leggere il suo codice sorgente. È anche un posto comodo per mettere le impostazioni predefinite.
Objective-C
- (void)initVC:(BOOL *)isDetailingEnabled {
self.isDetailingEnabled = isDetailingEnabled
}
veloce
func initVC(isDetailingEnabled: Bool) {
self.isDetailingEnabled = isDetailingEnabled
}
Uso del pattern Delegate (passaggio di dati indietro)
Per trasferire i dati dal controller di visualizzazione corrente al controller di visualizzazione precedente, è possibile utilizzare il modello delegato.
In questo esempio si presuppone che sia stato eseguito un seguito in Interface Builder e che si sia impostato l'identificatore di showSecondViewController
per showSecondViewController
. Le uscite e le azioni devono anche essere collegate ai nomi nel seguente codice.
First View Controller
Il codice per il controller First View è
veloce
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)
}
}
Objective-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
Nota l'uso del nostro protocollo personalizzato DataEnteredDelegate
.
Secondo View Controller and Protocol
Il codice per il secondo controller di vista è
veloce
// 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 ?? "")
}
}
Objective-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
Si noti che il protocol
è al di fuori della classe View Controller.
Passare i dati all'indietro usando lo svolgimento ai seguiti
A differenza di seguito che consente di passare i dati "in avanti" dal controller di visualizzazione corrente al controller di visualizzazione di destinazione:
(VC1) -> (VC2)
Usando "unwind" puoi fare il contrario, passare i dati dalla destinazione o dal controller della vista corrente al suo controller di visualizzazione presentando:
(VC1) <- (VC2)
NOTA : Prestare attenzione al fatto che l'utilizzo di unwind consente di passare prima i dati e successivamente il controller di visualizzazione corrente (VC2) verrà deallocato.
Ecco come farlo:
Innanzitutto, è necessario aggiungere la seguente dichiarazione al controller della vista di presentazione (VC1) che è il controller della vista che vogliamo trasmettere i dati a:
@IBAction func unwindToPresentingViewController(segue:UIStoryboardSegue)
L'importante è usare il prefisso unwind
, questo "informa" Xcode che questo è un metodo di svolgimento che ti dà la possibilità di usarlo anche nello storyboard.
Successivamente dovrai implementare il metodo, sembra quasi lo stesso di un seguito reale:
@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
}
Ora hai 2 opzioni per invocare le chiamate a piacimento:
- È possibile "hard code" richiamare:
self.performSegueWithIdentifier("YourCustomIdentifier", sender: self)
che eseguirà loperformSegueWithIdentifier
per te ogni volta cheperformSegueWithIdentifier
. - Puoi collegare il metodo di svolgimento utilizzando lo
storyboard
all'oggetto "Esci": ctrl + trascina il pulsante che vuoi richiamare il metodo di svolgimento, sull'oggetto "Esci":
Rilascia e avrai la possibilità di scegliere il tuo metodo di svolgimento personalizzato:
Trasmissione dei dati tramite le chiusure (trasmissione dei dati indietro)
Invece di usare il pattern delegato , che ha diviso l'implementazione in varie parti della classe UIViewController
, è possibile utilizzare anche le closures
per passare i dati indietro e avanti. Supponendo che tu stia utilizzando UIStoryboardSegue
, nel metodo prepareForSegue
puoi facilmente configurare il nuovo controller in un solo passaggio
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)
}
}
}
Questo è un esempio di utilizzo ed è meglio usare su Swift, la sintassi del blocco Objective-C non è così facile da rendere il codice più leggibile
Usando la chiusura (blocco) di richiamata che restituisce i dati
questo argomento è un problema classico nello sviluppo di iOS e la sua soluzione è diversa come già mostrato in altri esempi. In questo esempio mostrerò un altro uso quotidiano comune: passare i dati usando la closure
adattando l'esempio di delegate pattern
su questa pagina alla closure
callback!
una cosa che questo metodo è superiore a delegate pattern
è invece di dividere il codice di settaggio in due posti diversi (guarda l'esempio di delegato in questa pagina, prepareForSegue
, userDidEnterInformation
) piuttosto raccogliendoli insieme (solo in prepareForSegue
, lo mostrerò)
Inizia da Second View Controller
dobbiamo capire come usare il callback, quindi possiamo scriverlo, questo è il motivo per cui partiamo dal secondo controller di vista poiché è dove usiamo il callback: quando abbiamo ottenuto il nuovo input di testo, chiamiamo il nostro callback, usando come parametro il parametro callback per passare i dati al primo ViewController, notare che ho detto usando il parametro callback, questo è molto importante, i novizi (come lo ero io) lo ignorano sempre e non sanno da dove iniziare a scrivere correttamente la callback
quindi in questo caso, sappiamo che il nostro callback accetta solo un parametro: il testo e il suo tipo è String
, dichiariamolo e rendiamolo proprietà dal momento che abbiamo bisogno di compilare dal nostro primo controller di visualizzazione
Devo solo commentare tutta la parte del delegate
e tenerla per il confronto
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)
}
}
Termina il primo controller di visualizzazione
tutto quello che devi fare è passare la chiusura del callback, e abbiamo finito, la chiusura farà il lavoro futuro per noi dal momento che l'abbiamo già configurata in seconda visione
guarda come riduce il nostro codice rispetto al 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
//}
}
e nell'ultimo, forse qualcuno di voi sarà confuso dal fatto che stiamo passando solo i dati (chiusura in questo caso) solo in un modo, dal primo controller di visualizzazione al secondo, non direttamente dal controller di seconda visione, come possiamo considerarlo come uno strumento di comunicazione? forse dovresti davvero eseguirlo e provarlo tu stesso, tutto quello che dirò è il parametro , il parametro di callback closure che restituisce i dati indietro!
Assegnando una proprietà (Passa dati in avanti)
È possibile passare i dati direttamente assegnando la proprietà del controller di visualizzazione successivo prima di inviarlo o inviarlo.
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)")
}
}
}