Utilizzo di un'immagine personalizzata per l'accessorio UITableViewCell e la risposta a UITableViewDelegate


139

Sto usando un UITableViewCell personalizzato, incluso lo stesso per la cella accessoryView. La mia configurazione per accessorio View avviene in questo modo:

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];

Anche quando la cella viene inizializzata, utilizzando initWithFrame:reuseIdentifier:I assicurato per impostare la seguente proprietà:

self.userInteractionEnabled = YES;

Sfortunatamente nel mio UITableViewDelegate, il mio tableView:accessoryButtonTappedForRowWithIndexPath:metodo (prova a ripetere 10 volte) non viene attivato. Il delegato è sicuramente cablato correttamente.

Cosa può forse mancare?

Ringrazia tutti.

Risposte:


228

Purtroppo questo metodo non viene chiamato a meno che non si tocchi il tipo di pulsante interno fornito quando si utilizza uno dei tipi predefiniti. Per usare il tuo, dovrai creare il tuo accessorio come pulsante o altra sottoclasse UIControl (consiglierei un pulsante usando -buttonWithType:UIButtonTypeCustome impostando l'immagine del pulsante, piuttosto che usare UIImageView).

Ecco alcune cose che uso in Outpost, che personalizza abbastanza dei widget standard (solo leggermente, per abbinare la nostra colorazione verde acqua) che ho finito per fare la mia sottoclasse intermedia UITableViewController per contenere il codice di utilità per tutte le altre viste della tabella da usare (ora subclassano OPTableViewController).

Innanzitutto, questa funzione restituisce un nuovo pulsante di rivelazione dei dettagli utilizzando la nostra grafica personalizzata:

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

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

    return ( button );
}

Il pulsante chiamerà questa routine al termine, che quindi alimenta la routine standard UITableViewDelegate per i pulsanti accessori:

- (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];
}

Questa funzione individua la riga ottenendo la posizione nella vista tabella di un tocco dall'evento fornito dal pulsante e chiedendo alla vista tabella il percorso dell'indice della riga in quel punto.


Grazie Jim. È un peccato che ho passato più di 20 minuti a chiedermi perché non posso farlo con un imageView personalizzato. Ho appena visto come eseguire questa operazione sull'app Accessory di esempio di Apple. La tua risposta è ben spiegata e documentata, quindi la segnerò e la terrò in giro. Grazie ancora. :-)

Jim, ottima risposta. Un potenziale problema (almeno da parte mia) - Ho dovuto aggiungere la seguente riga per ottenere i tocchi per registrarmi sul pulsante: button.userInteractionEnabled = YES;
Mike Laurence,

11
Solo per gli altri che stanno guardando questa risposta, puoi anche mettere un tag sul pulsante che corrisponde alla riga (se hai più sezioni, dovrai fare un po 'di matematica) e quindi estrarre il tag dal pulsante in la funzione. Penso che potrebbe essere un po 'più veloce del calcolo del tocco.
RyanJM,

3
questo richiede di codificare il file self.tableView. cosa succede se non sai quale tableview contiene la riga?
user102008

4
@RyanJM Pensavo che fare un hitTest sia eccessivo e i tag saranno sufficienti. In effetti ho usato l'idea dei tag in alcuni dei miei codici. Ma oggi ho riscontrato un problema in cui l'utente può aggiungere nuove righe. Questo uccide l'hack usando i tag. La soluzione suggerita da Jim Dovey (e come si vede nel codice di esempio di Apple) è una soluzione generica e funziona in tutte le situazioni
srik

77

Ho trovato questo sito molto utile: visualizzazione degli accessori personalizzati per la tua uitableview in iphone

In breve, usa questo in 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;

quindi, implementa questo metodo:

- (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];
    }
}

4
Direi +1 per questo in quanto è ciò che Apple consiglia di fare nel loro codice di esempio nei loro documenti: developer.apple.com/library/ios/#samplecode/Accessory/Listings/…
cleverbit

L'impostazione della cornice era il pezzo mancante per me. Puoi anche solo impostareImage (anziché lo sfondo) purché non desideri anche alcun testo.
Jeremy Hicks,

1
Il link è stato interrotto nella risposta di @ richarddas. Nuovo link: developer.apple.com/library/prerelease/ios/samplecode/Accessory/…
delavega66,

7

Il mio approccio è quello di creare una UITableViewCellsottoclasse e incapsulare la logica che chiamerà il solito UITableViewDelegatemetodo al suo interno.

// 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

Questa è la risposta migliore. Ma button.superview, cell.superviewe [tableView.delegate tableView:...]non sono abbastanza sicuro.
Mr. Ming,

3

Un'estensione alla risposta di Jim Dovey sopra:

Fai attenzione quando usi un UISearchBarController con il tuo UITableView. In tal caso, si desidera verificare self.searchDisplayController.activee utilizzare self.searchDisplayController.searchResultsTableViewinvece diself.tableView . Altrimenti otterrai risultati imprevisti quando searchDisplayController è attivo, specialmente quando i risultati della ricerca vengono fatti scorrere.

Per esempio:

- (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];
}

2
  1. Definire una macro per i tag dei pulsanti:

    #define AccessoryViewTagSinceValue 100000 // (AccessoryViewTagSinceValue * sections + rows) must be LE NSIntegerMax
  2. Pulsante Crea e imposta cell.accessoryView durante la creazione di una cella

    UIButton *accessoryButton = [UIButton buttonWithType:UIButtonTypeContactAdd];
    accessoryButton.frame = CGRectMake(0, 0, 30, 30);
    [accessoryButton addTarget:self action:@selector(accessoryButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
    cell.accessoryView = accessoryButton;
  3. Impostare cell.accessoryView.tag di indexPath nel metodo UITableViewDataSource -tableView: cellForRowAtIndexPath:

    cell.accessoryView.tag = indexPath.section * AccessoryViewTagSinceValue + indexPath.row;
  4. Gestore di eventi per pulsanti

    - (void) accessoryButtonTapped:(UIButton *)button {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:button.tag % AccessoryViewTagSinceValue
                                                    inSection:button.tag / AccessoryViewTagSinceValue];
    
        [self.tableView.delegate tableView:self.tableView accessoryButtonTappedForRowWithIndexPath:indexPath];
    }
  5. Implementare il metodo UITableViewDelegate

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

1
Nessuno dovrebbe usare tagse non quando assolutamente necessario, cercare un'altra soluzione.
Lifely

2

Quando si tocca il pulsante, è possibile che venga chiamato il seguente metodo all'interno di una sottoclasse 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]];

 }

1

Con l'approccio yanchenko ho dovuto aggiungere: [accBtn setFrame:CGRectMake(0, 0, 20, 20)];

Se si utilizza il file xib per personalizzare tableCell, allora initWithStyle: reuseIdentifier: non verrà chiamato.

Sostituisci invece:

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

[super awakeFromNib];

}

1

È necessario utilizzare a UIControlper ottenere correttamente l'invio di eventi (ad esempio a UIButton) anziché semplice UIView/UIImageView.


1

Swift 5

Questo approccio utilizza il UIButton.tagper memorizzare indexPath usando lo spostamento di bit di base. L'approccio funzionerà su sistemi a 32 e 64 bit purché non ci siano più di 65535 sezioni o righe.

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

A partire da iOS 3.2 puoi evitare i pulsanti che altri qui stanno raccomandando e invece utilizzare UIImageView con un riconoscimento dei gesti di tocco. Assicurati di abilitare l'interazione dell'utente, che è disattivata per impostazione predefinita in UIImageViews.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.