Intégrer un UIButton sur une custom cell (transfert de data et segue)

JopaoneJopaone Membre
février 2016 modifié dans API UIKit #1

Bonjour,


 


Mon appli comporte un uitableviewcontroller pour lister des objets.


 


Chaque cellule sélectionnée mène à  un autre controller qui donne le détail de cet objet via un prepareForSegue avec l'identifier (J'utilise storyboard)


 


Je souhaiterais intégrer sur chaque cellule un uibutton qui, lorsqu'on le touche, mènerait vers un autre controller donnant le détail de l'auteur de l'objet.


 


Les cellules sont construites de la manière suivante :



- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *simpleTableIdentifier = @Cell;

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];

if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}

PFUser *bringobject = [object objectForKey:@toObject];

UILabel *nameLabel = (UILabel*) [cell viewWithTag:101];

nameLabel.text = [bringobject objectForKey:@name];

PFUser *bringAuthor = [object objectForKey:@fromUser];

PFFile *authorPicture = [bringAuthor objectForKey:@profilePicture];

PFImageView *PictureAuthor = (PFImageView*)[cell viewWithTag:105];

PictureAuthor.file = authorPicture;

[PictureAuthor loadInBackground];


...


 


 


mon segue se fait de cette manière pour aller vers le controller qui détaille l'objet :



if ([segue.identifier isEqualToString:@showObject])
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];

PFObject *object = [self.objects objectAtIndex:indexPath.row];

PFUser *user = object[@toObject];

detailViewController *svc = (detailViewController *)[segue destinationViewController];

svc.item = user;
}


si je le fais de cette autre manière je peux aller vers le controller qui détaille l'auteur de l'objet:



if ([segue.identifier isEqualToString:@showUser])
{
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];

PFObject *item = [self.objects objectAtIndex:indexPath.row];

PFUser *user = item[@author];

detailViewController *svc = (detailViewController *)[segue destinationViewController];

svc.user = user;
}


mais comme vous l'avez compris je souhaiterais que ce dernier segue se fasse via un uibutton.


 


Je fais donc face à  2 problèmes :


 


- comment intégrer un uitbutton sur une cellule (de type prototype cell /custom) et l'associer précisément à  l'objet se rapportant à  cette cellule ?


En faisant des recherches, jai pu voir qu'une méthode privilégiée serait d'associer le bouton à  un tag ?


 


- comment aller sur au autre controller en appuyant sur ce bouton ? Est-il possible de relier ce bouton au moyen d'un segue type push sur storyboard ? (j'ai l'impression que non ...)


 


qu'en pensez vous ? merci par avance pour votre aide !

Réponses

  • Tu pourrais créer créer ta propre classe UITableViewCell qui porterait toutes les infos dont l'objet et l'auteur, ainsi que tout tes IBOutlet


     


    Du coup tu lies une IBAction entre ton bouton et ta cellule et alors tu n'as qu'à  acceder à  la propriété qui t'interesse.



    #import "CustomTableViewCell.h"

    @implementation CustomTableViewCell

    @synthesize tonAuteur;
    @synthesize tonObjet;

    - (IBAction)onTapButton:(UIButton *)sender {
    // implementation de ton action
    }

    @end

    Ca ressemblerait à  ca


  • et ensuite on peu timaginer repasser la main au controlleur qui contient la tableview via un pattern type delegate.



    #import <UIKit/UIKit.h>

    @class CustomTableViewCell;

    @protocol CustomCellDelegate <NSObject>
    -(void) customCell:(CustomTableViewCell *)cell didSelectAuteur:(NSObject *) auteur;
    @end

    @interface CustomTableViewCell : UITableViewCell

    @property NSObject* tonObjet;
    @property NSObject* tonAuteur;
    @property id <CustomCellDelegate> delegate;

    @end

    et donc, quand tu configures ta cellule, tu leur affectes comme delegate le controlleur qui gere la table view. Comme ca dans l'implementation du tap sur le button tu fais 



    - (IBAction)onTapButton:(UIButton *)sender {
    // implementation de ton action
    [delegate customCell:self didSelectAuteur:tonAuteur];
    }

    Dans ton controlleur tu recuperes donc l'objet "tonAuteur" et tu n'as plus qu'a faire un performSegue en passant cet objet au destinationViewController


  • merci beaucoup pour ta réponse starmendo, j'essaye de mettre ça en place et te tiens au courant


  • Les blocks seraient à  mon sens plus judicieux même si les délégués marchent également.


    En créant une propriété comme block dans une Cellule Personnalisée tu pourrais démarrer le code que tu souhaites dans le cellForRowAtIndexPath.


  • Oui avec un bloc c'est plus simple à  mettre en place finalement.

    Apres si tu débutes en objective-C les blocks sont un peu obscurs au debut... Enfin je parle pour moi en tous cas :)
  • Joanna CarterJoanna Carter Membre, Modérateur
    février 2016 modifié #7

    J'ai fait un projet comme tutoriel, mais je mets ici quelques morceaux :



    // Cell.h

    @interface Cell : UITableViewCell

    + (NSString *)reuseIdentifier;

    @property (weak, nonatomic) NSDictionary *object;

    @property (copy, nonatomic) void (^objectEditedBlock) (NSDictionary *object);

    @end


    // Cell.m

    @interface Cell ()

    @property (weak, nonatomic) IBOutlet UILabel *label;

    @property (weak, nonatomic) IBOutlet UIImageView *image;

    - (IBAction)btnPress:(UIButton *)sender;

    @end



    @implementation Cell

    + (NSString *)reuseIdentifier;
    {
    return @Cell;
    }

    - (void)prepareForReuse
    {
    self.label.text = nil;

    self.image.image = nil;
    }

    - (void)setObject:(NSDictionary *)object
    {
    _object = object;

    PFUser *bringobject = [self.object objectForKey:@toObject];

    self.label.text = [bringobject objectForKey:@name];

    PFUser *bringAuthor = [object objectForKey:@fromUser];

    UIImage *authorPicture = [bringAuthor objectForKey:@profilePicture];

    self.image.image = authorPicture;
    }

    - (IBAction)btnPress:(UIButton *)sender
    {
    self.objectEditedBlock(self.object);
    }

    @end


    // ViewController.h

    @interface ViewController : UITableViewController

    @end


    // ViewController.m

    @interface ViewController ()

    @property (strong, nonatomic) NSDictionary *object;

    @end


    @implementation ViewController

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
    return 1;
    }

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    Cell *cell = [tableView dequeueReusableCellWithIdentifier:Cell.reuseIdentifier];

    cell.object = [NSDictionary dictionary];

    __weak typeof(self) weakSelf = self;

    cell.objectEditedBlock = ^(NSDictionary *object)
    {
    __strong typeof(weakSelf) strongSelf = weakSelf;

    if (strongSelf)
    {
    NSString *storyboardIdentifier = [AuthorViewController storyboardIdentifier];

    AuthorViewController *authorVC = [strongSelf.storyboard instantiateViewControllerWithIdentifier:storyboardIdentifier];

    authorVC.object = object;

    [strongSelf.navigationController pushViewController:authorVC animated:YES];
    }
    };

    return cell;
    }

    @end


    // AuthorViewController.h

    @interface AuthorViewController : UIViewController

    + (NSString *)storyboardIdentifier;

    @property (weak, nonatomic) NSDictionary *object;

    @end


    // AuthorViewController.m

    @implementation AuthorViewController

    + (NSString *)storyboardIdentifier
    {
    return @AuthorViewController;
    }

    - (void)viewDidLoad
    {
    [super viewDidLoad];

    // self.object has already been set

    // setup View
    }

    @end

    Tu trouveras, ci-joint, un zip du projet, afin que tu puisses voir les connections, etc. dans le storyboard.


  • Joanna CarterJoanna Carter Membre, Modérateur
    février 2016 modifié #8

    En plus, en utilisant un UITapGestureRecognizer sur la cellule, tu peux éviter le UIButton :



    // Cell.m

    @interface Cell ()

    @property (weak, nonatomic) IBOutlet UILabel *label;

    @property (weak, nonatomic) IBOutlet UIImageView *image;

    @end



    @implementation Cell

    - (instancetype)initWithCoder:(NSCoder *)aDecoder
    {
    self = [super initWithCoder:aDecoder];

    if (self)
    {
    UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTap)];

    [self addGestureRecognizer:tapRecognizer];
    }

    return self;
    }

    + (NSString *)reuseIdentifier;
    {
    return @Cell;
    }

    - (void)prepareForReuse
    {
    self.label.text = nil;

    self.image.image = nil;
    }

    - (void)setObject:(NSDictionary *)object
    {
    _object = object;

    PFUser *bringobject = [self.object objectForKey:@toObject];

    self.label.text = [bringobject objectForKey:@name];

    PFUser *bringAuthor = [object objectForKey:@fromUser];

    UIImage *authorPicture = [bringAuthor objectForKey:@profilePicture];

    self.image.image = authorPicture;
    }

    - (void)didTap
    {
    self.objectEditedBlock(self.object);
    }

    @end

  • Joanna CarterJoanna Carter Membre, Modérateur


    Les blocks seraient à  mon sens plus judicieux même si les délégués marchent également.


    En créant une propriété comme block dans une Cellule Personnalisée tu pourrais démarrer le code que tu souhaites dans le cellForRowAtIndexPath.




     


    Et, en plus, on peut capturer le IndexPath dans le block si nécessaire.

  • merci à  tous pour vos réponses, et un très grand merci pour ton aide Joanna Carter, ton projet m'est d'une très grande aide ! 


  • On pourrait aussi mettre le code du block dans la cellule.
    Cela obligerait à  passer un pointeur vers le controller qui devra présenter à  la cellule.
    Je ne sais pas si c'est mieux dans tous les cas, mais dans la mesure où la cellule est très spécialisée, on perd pas grand chose en réutilisation.
  • Joanna CarterJoanna Carter Membre, Modérateur


    On pourrait aussi mettre le code du block dans la cellule.

    Cela obligerait à  passer un pointeur vers le controller qui devra présenter à  la cellule.

    Je ne sais pas si c'est mieux dans tous les cas, mais dans la mesure où la cellule est très spécialisée, on perd pas grand chose en réutilisation.




     


    Si l'on faisait, ça limiterait le réutilisation de la cellule ailleurs. En outre, c'est pas le boulot d'une cellule de gérer la navigation entre les contrôleurs de vue ; ça c'est pour les UIViewControllers, qui tiennent déjà  leur UINavigationController.

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