web-dev-qa-db-fra.com

Utilisation d'une image personnalisée pour un accessoireViewable de UITableViewCell et réponse à UITableViewDelegate

J'utilise un UITableViewCell dessiné personnalisé, y compris le même pour le accessoryView de la cellule. Ma configuration pour l’accessoire se fait de la manière suivante:

UIImage *accessoryImage = [UIImage imageNamed:@"accessoryDisclosure.png"];
UIImageView *accImageView = [[UIImageView alloc] initWithImage:accessoryImage];
accImageView.userInteractionEnabled = YES;
[accImageView setFrame:CGRectMake(0, 0, 28.0, 28.0)];
self.accessoryView = accImageView;
[accImageView release];

Également lorsque la cellule est initialisée, en utilisant initWithFrame:reuseIdentifier: Je me suis assuré de définir la propriété suivante:

self.userInteractionEnabled = YES;

Malheureusement, dans mon UITableViewDelegate, mon tableView:accessoryButtonTappedForRowWithIndexPath: méthode (essayez de répéter cela 10 fois) ne se déclenche pas. Le délégué est définitivement câblé correctement.

Que peut-il éventuellement manquer?

Merci a tous.

138
anon

Malheureusement, cette méthode n'est appelée que si le type de bouton interne fourni lorsque vous utilisez l'un des types prédéfinis est sélectionné. Pour utiliser les vôtres, vous devez créer votre accessoire sous forme de bouton ou de sous-classe UIControl (je recommanderais un bouton utilisant -buttonWithType:UIButtonTypeCustom et en définissant l'image du bouton, plutôt que d'utiliser un UIImageView).

Voici quelques éléments que j'utilise dans Outpost, qui personnalise assez de widgets standard (juste un peu, pour correspondre à notre couleur turquoise) que j'ai finalement créés dans ma propre sous-classe intermédiaire UITableViewController OPTableViewController).

Tout d'abord, cette fonction renvoie un nouveau bouton de divulgation des détails à l'aide de notre graphique personnalisé:

- (UIButton *) makeDetailDisclosureButton
{
    UIButton * button = [UIButton outpostDetailDisclosureButton];

[button addTarget: self
               action: @selector(accessoryButtonTapped:withEvent:)
     forControlEvents: UIControlEventTouchUpInside];

    return ( button );
}

Le bouton appellera cette routine à la fin, qui alimentera ensuite la routine standard UITableViewDelegate pour les boutons accessoires:

- (void) accessoryButtonTapped: (UIControl *) button withEvent: (UIEvent *) event
{
    NSIndexPath * indexPath = [self.tableView indexPathForRowAtPoint: [[[event touchesForView: button] anyObject] locationInView: self.tableView]];
    if ( indexPath == nil )
        return;

    [self.tableView.delegate tableView: self.tableView accessoryButtonTappedForRowWithIndexPath: indexPath];
}

Cette fonction localise la ligne en obtenant l'emplacement dans la vue table d'une touche à partir de l'événement fourni par le bouton et en demandant à la vue table le chemin d'index de la ligne à ce point.

228
Jim Dovey

J'ai trouvé ce site très utile: vue accessoire personnalisée pour votre uitableview dans iphone

En bref, utilisez ceci dans cellForRowAtIndexPath::

UIImage *image = (checked) ? [UIImage imageNamed:@"checked.png"] : [UIImage imageNamed:@"unchecked.png"];

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
CGRect frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
button.frame = frame;
[button setBackgroundImage:image forState:UIControlStateNormal];

[button addTarget:self action:@selector(checkButtonTapped:event:)  forControlEvents:UIControlEventTouchUpInside];
button.backgroundColor = [UIColor clearColor];
cell.accessoryView = button;

alors, implémentez cette méthode:

- (void)checkButtonTapped:(id)sender event:(id)event
{
    NSSet *touches = [event allTouches];
    UITouch *touch = [touches anyObject];
    CGPoint currentTouchPosition = [touch locationInView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint: currentTouchPosition];

    if (indexPath != nil)
    {
        [self tableView: self.tableView accessoryButtonTappedForRowWithIndexPath: indexPath];
    }
}
77
Jon

Mon approche consiste à créer une sous-classe UITableViewCell et à encapsuler la logique qui appellera la méthode habituelle de UITableViewDelegate.

// CustomTableViewCell.h
@interface CustomTableViewCell : UITableViewCell

- (id)initForIdentifier:(NSString *)reuseIdentifier;

@end

// CustomTableViewCell.m
@implementation CustomTableViewCell

- (id)initForIdentifier:(NSString *)reuseIdentifier;
{
    // the subclass specifies style itself
    self = [super initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:reuseIdentifier];
    if (self) {
        // get the button elsewhere
        UIButton *accBtn = [ViewFactory createTableViewCellDisclosureButton];
        [accBtn addTarget: self
                   action: @selector(accessoryButtonTapped:withEvent:)
         forControlEvents: UIControlEventTouchUpInside];
        self.accessoryView = accBtn;
    }
    return self;
}

#pragma mark - private

- (void)accessoryButtonTapped:(UIControl *)button withEvent:(UIEvent *)event
{
    UITableViewCell *cell = (UITableViewCell*)button.superview;
    UITableView *tableView = (UITableView*)cell.superview;
    NSIndexPath *indexPath = [tableView indexPathForCell:cell];
    [tableView.delegate tableView:tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
}

@end
7
yanchenko

Une extension de la réponse de Jim Dovey ci-dessus:

Soyez prudent lorsque vous utilisez un UISearchBarController avec votre UITableView. Dans ce cas, vous voulez vérifier self.searchDisplayController.active et utilise self.searchDisplayController.searchResultsTableViewau lieu de self.tableView. Sinon, vous obtiendrez des résultats inattendus lorsque searchDisplayController est actif, notamment lorsque les résultats de la recherche défilent.

Par exemple:

- (void) accessoryButtonTapped:(UIControl *)button withEvent:(UIEvent *)event
{
    UITableView* tableView = self.tableView;
    if(self.searchDisplayController.active)
        tableView = self.searchDisplayController.searchResultsTableView;

    NSIndexPath * indexPath = [tableView indexPathForRowAtPoint:[[[event touchesForView:button] anyObject] locationInView:tableView]];
    if(indexPath)
       [tableView.delegate tableView:tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
}
3
Zaggo
  1. Définissez une macro pour les balises de boutons:

    #define AccessoryViewTagSinceValue 100000 // (AccessoryViewTagSinceValue * sections + rows) must be LE NSIntegerMax
    
  2. Créer un bouton et définir cell.accessoryView lors de la création d'une cellule

    UIButton *accessoryButton = [UIButton buttonWithType:UIButtonTypeContactAdd];
    accessoryButton.frame = CGRectMake(0, 0, 30, 30);
    [accessoryButton addTarget:self action:@selector(accessoryButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
    cell.accessoryView = accessoryButton;
    
  3. Définissez cell.accessoryView.tag avec indexPath dans la méthode UITableViewDataSource -tableView: cellForRowAtIndexPath:

    cell.accessoryView.tag = indexPath.section * AccessoryViewTagSinceValue + indexPath.row;
    
  4. Gestionnaire d'événements pour les boutons

    - (void) accessoryButtonTapped:(UIButton *)button {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:button.tag % AccessoryViewTagSinceValue
                                                    inSection:button.tag / AccessoryViewTagSinceValue];
    
        [self.tableView.delegate tableView:self.tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
    }
    
  5. Implémenter la méthode UITableViewDelegate

    - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
        // do sth.
    }
    
2
iwill

Lorsque le bouton est exploité, vous pouvez le faire appeler la méthode suivante dans une sous-classe UITableViewCell

 -(void)buttonTapped{
     // perform an UI updates for cell

     // grab the table view and notify it using the delegate
     UITableView *tableView = (UITableView *)self.superview;
     [tableView.delegate tableView:tableView accessoryButtonTappedForRowWithIndexPath:[tableView indexPathForCell:self]];

 }
2
Eric Welander

Vous devez utiliser un UIControl pour obtenir correctement la répartition d’événement (par exemple un UIButton) au lieu de simple UIView/UIImageView.

1
ikarius

Avec l'approche Yanchenko, j'ai dû ajouter: [accBtn setFrame:CGRectMake(0, 0, 20, 20)];

Si vous utilisez un fichier xib pour personnaliser votre tableCell, initWithStyle: reuseIdentifier: ne sera pas appelé.

Au lieu de remplacer:

-(void)awakeFromNib
{
//Put your code here 

[super awakeFromNib];

}
1
Toydor

Depuis iOS 3.2, vous pouvez éviter les boutons recommandés par d’autres utilisateurs et utiliser votre UIImageView avec un outil de reconnaissance des gestes tactiles. Veillez à activer l'interaction utilisateur, désactivée par défaut dans UIImageViews.

0
aeu

Swift 5

Cette approche utilise le UIButton.tag pour stocker le chemin indexPath en utilisant le transfert de bits de base. L'approche fonctionnera sur les systèmes 32 et 64 bits tant que vous n'avez pas plus de 65 535 sections ou lignes.

public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: "cellId")
    let accessoryButton = UIButton(type: .custom)
    accessoryButton.setImage(UIImage(named: "imageName"), for: .normal)
    accessoryButton.sizeToFit()
    accessoryButton.addTarget(self, action: #selector(handleAccessoryButton(sender:)), for: .touchUpInside)

    let tag = (indexPath.section << 16) | indexPath.row
    accessoryButton.tag = tag
    cell?.accessoryView = accessoryButton

}

@objc func handleAccessoryButton(sender: UIButton) {
    let section = sender.tag >> 16
    let row = sender.tag & 0xFFFF
    // Do Stuff
}
0
Brody Robertson