Objective c - recupérer les élements d'une classe dans une DetailViewController

avril 2015 modifié dans API UIKit #1

Bonsoir;


 J'essaye de récupérer les élèments du tableViewCell dans une DetailViewController.


 


DetailVoitureViewController.h



#import <UIKit/UIKit.h>
#import "Voiture.h"


@interface DetailVoitureViewController : UIViewController

@property (nonatomic,strong) IBOutlet UILabel *marque;
@property (nonatomic,strong) IBOutlet UILabel *modele;
@property (nonatomic, strong) IBOutlet UILabel *prix;

@property (nonatomic,strong) Voiture *detailVoiture;

@end


dans le DetailVoitureViewController.m



-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

if ([[segue identifier] isEqualToString:@detailVoitureSegment]) {
DetailVoitureViewController *detailvoiture = [segue destinationViewController];

NSIndexPath *monIndex = [[self tableView] indexPathForSelectedRow];

Voiture *voitureChoisie = [[_sections valueForKey:[[[_sections allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] objectAtIndex:monIndex.section]] objectAtIndex:monIndex.row];

detailvoiture.detailVoiture = [[Voiture alloc] initWithMarque:voitureChoisie.marque withModele:voitureChoisie.modele withPrix:voitureChoisie.prix];
}
}


A la compilation la priprièty  detailVoiture n' est pas reconnue :


Mots clés:

Réponses

  • LeChatNoirLeChatNoir Membre, Modérateur

    Pourquoi allouer un nouvel objet Voiture ?


     


    DetailVoitureViewController


     


    devrait avoir une propriété detailVoiture de type Voiture.


     


    Et ainsi, tu fais un simple :



    if ([[segue identifier] isEqualToString:@detailVoitureSegment]) {
    DetailVoitureViewController *detailvoitureCtrl = [segue destinationViewController];
    NSIndexPath *monIndex = [[self tableView] indexPathForSelectedRow];
    Voiture *voitureChoisie = [[_sections valueForKey:[[[_sections allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)] objectAtIndex:monIndex.section]] objectAtIndex:monIndex.row];
    detailvoitureCtrl.detailVoiture = voitureChoisie;
    }
  • Joanna CarterJoanna Carter Membre, Modérateur
    avril 2015 modifié #3

    Tout d'abord, je te demanderais, pourquoi tu écris le code pour prepareForSegue: dans la classe DetailVoitureController, ou voulais-tu dire MasterViewController ?


     


    Puis, le code en général, c'est plein de fautes.


     


    1. On ne met pas les IBOutlets dans le fichier .h. C'est mieux de les mettre dans une extension de la classe dans le fichier .m


     


    2. On ne met pas les IBOutlets en strong ; selon les docs il faut les mettre en weak, parce que il y a déjà  une référence strong dans le storyboard/xib



    // DetailVoitureViewController.h

    @interface DetailVoitureViewController : UIViewController

    @property (nonatomic, strong) Voiture *detailVoiture;

    @end


    // DetailVoitureViewController.m

    @interface DetailVoitureViewController ()

    @property (nonatomic, weak) IBOutlet UILabel *marque;
    @property (nonatomic, weak) IBOutlet UILabel *modele;
    @property (nonatomic, weak) IBOutlet UILabel *prix;

    @end

    Après ça, le code est un peu bordel. Tu utilises "_sections" qui, je devine, est une ivar en arrière d'une propriété ; c'est pas conseillé pour, au moins, deux raisons :


     


    1. On ne devrait pas accéder les ivars sauf dans les méthodes d'initialisation ou dans la méthode dealloc ; tu devrais accéder seulement la propriété ailleurs.


     


    2. Le nom n'indique pas ceux qui est contenu là  dedans. Je devine c'est un dictionnaire de listes, dont, le premier niveau c'est les marques, le deuxième c'est les modèles.


     


    J'ai raison ?


     


    Si c'était le cas, il vaudrait mieux d'utiliser le nom "marques" pour la liste des marques.


     


    En plus, il faut utiliser les variables intermédiaires pour "désemballer" les marques, puis les modèles, afin que le code soit plus lisible quand tu y reviens à  l'avenir.


     


    N'utilises pas le même nom pour une contrôleur que pour une instance d'une classe e.g. detailVoiture pour la contrôleur et pareil pour la propriété qui tient la Voiture.


     


    Il y a plus à  dire mais, avec ton code dans l'état qu'il se trouve, c'est difficile à  déterminer ce que tu veux dire.


  • avril 2015 modifié #4


     


    Pourquoi allouer un nouvel objet Voiture ?


     


    DetailVoitureViewController


     


    devrait avoir une propriété detailVoiture de type Voiture.


     


    Et ainsi, tu fais un simple :







    la proprièté detailVoiture existe je l'ai déclarer dans le ficher 'DetailVoitureViewController.h.




    #import <UIKit/UIKit.h>
    #import "Voiture.h"

    @interface DetailVoitureViewController : UIViewController

    @property (nonatomic, strong) IBOutlet UILabel *marque;
    @property (nonatomic, strong) IBOutlet UILabel *modele;
    @property (nonatomic, strong) IBOutlet UILabel *prix;

    @property (nonatomic,strong) Voiture *detailVoiture;




    @end


  • Joanna CarterJoanna Carter Membre, Modérateur

    la proprièté detailVoiture existe je l'ai déclarer dans le ficher 'DetailVoitureViewController.h.


    #import <UIKit/UIKit.h>
    #import "Voiture.h"

    @interface DetailVoitureViewController : UIViewController

    @property (nonatomic, strong) IBOutlet UILabel *marque;
    @property (nonatomic, strong) IBOutlet UILabel *modele;
    @property (nonatomic, strong) IBOutlet UILabel *prix;

    @property (nonatomic,strong) Voiture *detailVoiture;




    @end




    Je note que tu n'as pas encore :


    1. répondu à  mes questions


    2. déplacé les IBOutlets dans l'extension de la classe dans le fichier .m


    3. mis les IBOutlets en weak


    Je crois que le chat t'a demandé pourquoi t'as alloué un objet Voiture parce que c'est assez rare à  n'avoir pas encore le créer avant de faire le choix de marque, modèle, etc.
  • MalaMala Membre, Modérateur

    Stéphane, merci de prendre en considération les observations de Joanna et du chat noir. Répondre à  ses interlocuteurs est un gage de politesse d'autant qu'ils mettent ici l'accent sur des lacunes qui risquent de te porter préjudice dans ta progression.


     


    PS: piqure de rappel au passage pour les débutants en général, se présenter dans la section adéquat est aussi important. On ne prend pas forcément le temps de vous accueillir de vive voix mais, lorsque vous posez vos premières questions, on y passe quasi systématiquement. Cela nous permet d'estimer vos bagages et d'adapter notre discours en conséquence lorsqu'on vous répond.



  •  


     


    1. On ne met pas les IBOutlets dans le fichier .h. C'est mieux de les mettre dans une extension de la classe dans le fichier .m

    C'est la première fois que j'entends cette remarque dont la raison reste mystérieuse pour moi. Si quelqu'un peux m'en dire plus je le remercie d'avance. Est-ce valable pour tous les IBOutlet ou seulement pour ceux définis comme @property ?


  • On ne met dans le .h que les IBOutlets qui sont aussi des propriétés que l'on souhaite rendre accessibles aux autres classes.


    La plupart des IBOutlets peuvent rester privés.


  • AliGatorAliGator Membre, Modérateur
    avril 2015 modifié #9
    C'est une question d'encapsulation.

    Les IBOutlets n'ont pas à  être exposés à  l'extérieur, il n'y a pas de raison qu'ils le soient, donc c'est mieux de les garder privés pour éviter d'être tenté de les modifier de l'extérieur. Qu'elle soit sous forme de @property ou de variable d'instance (mais qui utilise encore des variables d'instances ?!) n'y change rien.

    En effet, pour respecter le principe d'encapsulation, une Vue n'a pas à  exposer à  l'extérieur comment elle est composée, l'extérieur n'a à  fournir à  la vue que les informations à  afficher, ce n'est pas à  l'extérieur de savoir comment afficher ces informations.

    C'est le principe des patterns MVC et MVVM.

    ---

    Exemple : imaginons que tu aies besoin d'une vue qui es sensée afficher un texte et un état "OK" ou "KO".

    Tu n'as donc besoin de n'exposer dans les @property de ton ".h" que le texte à  afficher, et un booléen pour dire si ça a réussi ou non. L'extérieur de la vue (les autres classes / ViewControllers) n'ont pas à  savoir comment la vue est composée au niveau de l'UI (donc tu n'as pas à  exposer ces IBOutlet dans ton ".h").

    En effet, si aujourd'hui tu utilises un UILabel pour afficher ton texte, et une UIView avec une backgroundColor verte ou rouge pour indiquer l'état OK/KO, peut-être que demain tu utiliseras autre chose, comme un UITextView pour le texte et une UIImageView avec une image pour indiquer l'état OK/KO. C'est d'ailleurs un intérêt de MVC ou MVVM, séparer la vue, la partie liée à  l'UI et à  ton interface graphique, des données que tu veux représenter.

    Donc tu vas plutôt exposer dans ton ".h" les propriétés "NSString* message" et "BOOL success", mais tu vas garder les IBOutlets "UILabel* messageLabel" et "UIView* statusView" en privé dans ton ".m", sans les exposer dans ton .h, comme ça tu es sûr que tu ne risques pas d'utiliser ces IBOutlets depuis l'extérieur de ta classe (ainsi tu ne sera pas tenté de manipuler directement ton messageLabel depuis ton modèle ou depuis un autre ViewController).

    Et si demain tu changes ta UIView avec backgroundColor en une UIImageView avec image pour représenter l'état OK/KO, tu n'auras à  changer QUE l'implémentation, donc ton .m, et tu ne changeras pas l'API / l'interface (ton ".h") de ta classe. Ce qui est important car ça sera alors totalement transparent pour le reste de ton code : tu n'auras aucun refactoring à  faire en dehors de ta classe pour faire ce changement d'UI.
    Alors que si tu avais exposé des IBOutlets dans ton ".h", ça veut dire que quelque part ailleurs dans ton code peut-être que quelqu'un se serait permis de directement faire "tonVC.statusView.backgroundColor = [UIColor blueColor];" au lieu de faire "tonVC.success = YES", ce qui non seulement montre que tu permets ainsi à  du code extérieur de mettre une couleur de fond qui n'était pas prévue à  la base (bleu et non pas vert ou rouge), mais en plus si tu refactor pour mettre une UIImageView avec image et que tu oublies de modifier le code complètement ailleurs dans ton appli qui fait cet accès direct à  statusView, tu risques de te retrouver avec une UIImageView ayant une couleur de fond et pas d'image...


    Voilà  pourquoi il vaut mieux éviter d'exposer les IBOutlets dans le .h, tout comme n'importe quelle autre variable d'instance ou propriété qui n'a aucune raison d'être exposée à  l'extérieur.
  • Merci pour tes explications.


  • C'est l'explication la plus fonctionnelle aux variables privées que j'ai lue. Je... comment dites-vous? "plussoie"? 


Connectez-vous ou Inscrivez-vous pour répondre.