iOS
Transmission de données entre les contrôleurs de vue
Recherche…
Utilisation de Segues (transmission de données en avant)
Pour transmettre des données du contrôleur de vue actuel au nouveau contrôleur de vue suivant (pas à un contrôleur de vue précédent) à l'aide de segues, créez d'abord un lien avec un identifiant dans le storyboard correspondant. Remplacez la méthode prepareForSegue
votre contrôleur d'affichage actuel. À l’intérieur de la méthode, vérifiez le segment que vous venez de créer grâce à son identifiant. Lancez le contrôleur de vue de destination et transmettez-lui les données en définissant les propriétés sur le contrôleur de la vue réduite.
Définition d'un identifiant pour un segue:
Les segues peuvent être exécutées par programme ou en utilisant les actions de bouton définies dans le storyboard par Ctrl + Glisser vers le contrôleur de vue de destination. En cas de besoin, vous pouvez appeler un segue par programme en utilisant l'identifiant de segue dans le contrôleur de vue:
Objectif c
- (void)showDetail {
[self performSegueWithIdentifier:@"showDetailingSegue" sender:self];
}
Rapide
func showDetail() {
self.performSegue(withIdentifier: "showDetailingSegue", sender: self)
}
Vous pouvez configurer la charge utile segue dans la version surchargée de la méthode prepareForSegue
. Vous pouvez définir les propriétés requises avant le chargement du contrôleur de vue de destination.
Objectif c
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:@"showDetailingSegue"]){
DetailViewController *controller = (DetailViewController *)segue.destinationViewController;
controller.isDetailingEnabled = YES;
}
}
Rapide
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetailingSegue" {
let controller = segue.destinationViewController as! DetailViewController
controller.isDetailingEnabled = true
}
}
DetailViewController
est le nom du second contrôleur de vue et isDetailingEnabled
est une variable publique dans ce contrôleur de vue.
Pour développer ce modèle, vous pouvez traiter une méthode publique sur DetailViewController
tant que pseudo-initialiseur, afin d’initialiser les variables requises. Cela va auto-documenter les variables qui doivent être définies sur DetailViewController
sans avoir à lire son code source. C'est aussi un endroit pratique pour mettre des défauts.
Objectif c
- (void)initVC:(BOOL *)isDetailingEnabled {
self.isDetailingEnabled = isDetailingEnabled
}
Rapide
func initVC(isDetailingEnabled: Bool) {
self.isDetailingEnabled = isDetailingEnabled
}
Utilisation du modèle de délégué (retour des données)
Pour renvoyer des données du contrôleur de vue actuel au contrôleur de vue précédent, vous pouvez utiliser le modèle de délégué.
Cet exemple suppose que vous avez effectué un segue dans le showSecondViewController
interface et que vous définissez l'identificateur de segue sur showSecondViewController
. Les points de vente et les actions doivent également être reliés aux noms dans le code suivant.
Premier contrôleur d'affichage
Le code du contrôleur First View est
Rapide
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)
}
}
Objectif 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
Notez l'utilisation de notre protocole personnalisé DataEnteredDelegate
.
Second View Controller et Protocole
Le code du second contrôleur de vue est
Rapide
// 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 ?? "")
}
}
Objectif 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
Notez que le protocol
est en dehors de la classe View Controller.
Passer des données en arrière en utilisant le déroulement pour se
Contrairement à segue qui vous permet de transmettre des données "en avant" du contrôleur de vue actuel au contrôleur de vue de destination:
(VC1) -> (VC2)
En utilisant "dérouler", vous pouvez faire le contraire, passer des données de la destination ou du contrôleur de vue actuel à son contrôleur de vue présentateur:
(VC1) <- (VC2)
REMARQUE : faites attention à ce que l'utilisation de dérouler vous permette de transmettre les données en premier, puis le contrôleur de vue actuel (VC2) sera libéré.
Voici comment procéder:
Tout d'abord, vous devrez ajouter la déclaration suivante au contrôleur de vue de présentation (VC1), qui est le contrôleur de vue que vous souhaitez transmettre aux données:
@IBAction func unwindToPresentingViewController(segue:UIStoryboardSegue)
L'important est d'utiliser le préfixe unwind
, cela "informe" Xcode qu'il s'agit d'une méthode de déroulage vous donnant la possibilité de l'utiliser également dans le storyboard.
Ensuite, vous devrez implémenter la méthode, elle ressemble presque à un segue réel:
@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
}
Vous avez maintenant 2 options pour appeler les appels à la fin:
- Vous pouvez « coder en dur » invoquer le:
self.performSegueWithIdentifier("YourCustomIdentifier", sender: self)
qui fera le dérouleur pour vous à chaque fois que vous voulezperformSegueWithIdentifier
. - Vous pouvez lier la méthode de déroulement en utilisant le
storyboard
à votre objet "Exit": ctrl + faites glisser le bouton que vous voulez appeler la méthode déroulante vers l'objet "Exit":
Relâchez et vous aurez la possibilité de choisir votre méthode de déroulement personnalisée:
Transmission de données à l'aide de fermetures (retour de données)
Au lieu d'utiliser le modèle de délégué , qui divise l'implémentation dans différentes parties de la classe UIViewController
, vous pouvez même utiliser des closures
pour renvoyer des données en avant et en arrière. En supposant que vous utilisez le UIStoryboardSegue
, dans la méthode prepareForSegue
, vous pouvez facilement configurer le nouveau contrôleur en une seule étape
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)
}
}
}
Ceci est un exemple d'utilisation et il est préférable d'utiliser Swift, la syntaxe du bloc Objective-C n'est pas si facile de rendre le code plus lisible
Utiliser la fermeture de rappel (blocage) pour transmettre des données
ce sujet est un problème classique dans le développement iOS, et sa solution est diverse comme d'autres exemples déjà présentés. Dans cet exemple, je vais vous montrer une autre utilisation quotidienne commune: transmettre des données à l'aide de la closure
en adaptant l'exemple de delegate pattern
sur cette page en closure
rappel!
Une chose que cette méthode est supérieure à delegate pattern
est au lieu de diviser le code de configuration en deux endroits différents (regardez l'exemple de délégué sur cette page, prepareForSegue
, userDidEnterInformation
) plutôt que de les rassembler (seulement dans prepareForSegue
, je le montrerai)
Démarrer à partir de Second View Controller
nous devons comprendre comment utiliser le callback, puis pouvons-nous l'écrire, c'est pourquoi nous commençons à partir du second contrôleur de vue car nous utilisons callback: lorsque nous avons la nouvelle entrée de texte, nous appelons notre callback, en utilisant le paramètre callback comme support pour renvoyer les données au premier ViewController, notez que j'ai dit en utilisant le paramètre callback, c'est très important, les novices (comme je l'étais) oublient toujours cela et ne savent pas par où commencer pour écrire la fermeture de rappel correctement
donc dans ce cas, nous savons que notre callback ne prend qu'un seul paramètre: text et que son type est String
, déclarons-le et en faisons la propriété puisque nous avons besoin de remplir notre premier contrôleur de vue
Je ne fais que commenter la partie delegate
et la conserver pour la comparer
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)
}
}
Terminer le contrôleur de la première vue
tout ce que vous avez à faire est de passer la fermeture de rappel, et nous avons terminé, la fermeture fera le travail futur pour nous puisque nous l'avons déjà configuré dans le contrôleur de deuxième vue
regardez comment cela rend notre code plus court comparé au 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
//}
}
et dans le dernier cas, peut-être quelqu'un d'entre vous sera-t-il confus en pensant que nous ne transmettons que les données (fermeture dans ce cas) d'une seule manière, considère-le comme un outil de communication? peut-être devriez-vous vraiment l'exécuter et le prouver vous-même, tout ce que je dirai, c'est le paramètre , c'est le paramètre de la fermeture de rappel qui renvoie les données!
En attribuant une propriété (transmission de données vers l'avant)
Vous pouvez transmettre des données directement en assignant la propriété du contrôleur de vue suivant avant de le pousser ou de le présenter.
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)")
}
}
}