Verwenden eines benutzerdefinierten Abbilds für die Zubehöransicht einer UITableViewCell und deren Reaktion auf UITableViewDelegate

139

Ich verwende eine benutzerdefinierte gezeichnete UITableViewCell, einschließlich derselben für die Zelle accessoryView. Mein Setup für die Zubehöransicht erfolgt folgendermaßen:

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

Auch bei der Initialisierung der Zelle wurde mit initWithFrame:reuseIdentifier:I sichergestellt, dass die folgende Eigenschaft festgelegt wird:

self.userInteractionEnabled = YES;

Leider wird in meinem UITableViewDelegate meine tableView:accessoryButtonTappedForRowWithIndexPath:Methode (versuchen Sie das 10 Mal zu wiederholen) nicht ausgelöst. Der Delegierte ist definitiv richtig verkabelt.

Was kann möglicherweise fehlen?

Vielen Dank an alle.


quelle

Antworten:

228

Leider wird diese Methode nur aufgerufen, wenn der interne Schaltflächentyp, der bei Verwendung eines der vordefinierten Typen angegeben wird, getippt wird. Um Ihr eigenes Zubehör zu verwenden, müssen Sie Ihr Zubehör als Schaltfläche oder andere UIControl-Unterklasse erstellen (ich würde die Verwendung einer Schaltfläche empfehlen-buttonWithType:UIButtonTypeCustom und das Bild der Schaltfläche festzulegen, anstatt eine UIImageView zu verwenden).

Hier sind einige Dinge, die ich in Outpost verwende, wodurch die Standard-Widgets so angepasst werden (nur geringfügig, dass sie unserer blaugrünen Farbe entsprechen), dass ich meine eigene UITableViewController-Zwischenunterklasse erstellt habe, um den Dienstprogrammcode für alle anderen zu verwendenden Tabellenansichten zu speichern (sie sind jetzt Unterklassen) OPTableViewController).

Erstens gibt diese Funktion mithilfe unserer benutzerdefinierten Grafik eine neue Schaltfläche zur Offenlegung von Details zurück:

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

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

    return ( button );
}

Die Schaltfläche ruft diese Routine auf, wenn sie fertig ist, und speist dann die Standardroutine UITableViewDelegate für zusätzliche Schaltflächen:

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

Diese Funktion lokalisiert die Zeile, indem sie die Position in der Tabellenansicht einer Berührung aus dem von der Schaltfläche bereitgestellten Ereignis abruft und die Tabellenansicht an diesem Punkt nach dem Indexpfad der Zeile fragt.

Jim Dovey
quelle
Danke Jim. Schade, dass ich mich mehr als 20 Minuten lang gefragt habe, warum ich das mit einer benutzerdefinierten Bildansicht nicht machen kann. Ich habe gerade gesehen, wie das in Apples Beispiel-Zubehör-App geht. Ihre Antwort ist jedoch gut erklärt und dokumentiert, daher markiere ich sie und behalte sie bei. Danke noch einmal. :-)
Jim, tolle Antwort. Ein potenzielles Problem (zumindest an meinem Ende) - Ich musste die folgende Zeile hinzufügen, damit sich die Berührungen auf der Schaltfläche registrieren konnten: button.userInteractionEnabled = YES;
Mike Laurence
11
Nur für andere, die sich diese Antwort ansehen, können Sie auch einfach ein Tag auf die Schaltfläche setzen, das der Zeile entspricht (wenn Sie mehrere Abschnitte haben, müssen Sie etwas rechnen) und dann das Tag einfach aus der Schaltfläche herausziehen die Funktion. Ich denke, es könnte etwas schneller sein als die Berechnung der Berührung.
RyanJM
3
Dies erfordert, dass Sie die fest codieren self.tableView. Was ist, wenn Sie nicht wissen, welche Tabellenansicht die Zeile enthält?
user102008
4
@RyanJM Früher dachte ich, dass ein HitTest übertrieben ist und Tags ausreichen. Ich habe tatsächlich die Tags-Idee in einigen meiner Codes verwendet. Aber heute bin ich auf ein Problem gestoßen, bei dem der Benutzer neue Zeilen hinzufügen kann. Dies beendet den Hack mit Tags. Die von Jim Dovey vorgeschlagene Lösung (und wie im Beispielcode von Apple zu sehen) ist eine generische Lösung und funktioniert in allen Situationen
srik
77

Ich fand diese Website sehr hilfreich: Benutzerdefinierte Zubehöransicht für Ihre uitableview in iphone

Kurz gesagt, verwenden Sie dies 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;

Implementieren Sie dann diese Methode:

- (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];
    }
}
Jon
quelle
4
Ich würde +1 dafür sagen, da Apple dies in seinem Beispielcode in den Dokumenten empfiehlt: developer.apple.com/library/ios/#samplecode/Accessory/Listings/…
Cleverbit
Das Einstellen des Rahmens war für mich das fehlende Teil. Sie können auch nurImage (anstelle des Hintergrunds) festlegen, solange Sie keinen Text möchten.
Jeremy Hicks
1
Der Link ist in der Antwort von @ richarddas unterbrochen. Neuer Link: developer.apple.com/library/prerelease/ios/samplecode/Accessory/…
delavega66
7

Mein Ansatz besteht darin, eine UITableViewCellUnterklasse zu erstellen und die Logik zu kapseln, die die übliche UITableViewDelegateMethode darin aufruft.

// 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
yanchenko
quelle
Dies ist die beste Antwort. Aber button.superview, cell.superviewund [tableView.delegate tableView:...]ist nicht sicher genug.
Herr Ming
3

Eine Erweiterung der obigen Antwort von Jim Dovey:

Seien Sie vorsichtig, wenn Sie einen UISearchBarController mit Ihrem UITableView verwenden. In diesem Fall möchten Sie überprüfen self.searchDisplayController.activeund verwenden self.searchDisplayController.searchResultsTableViewstatt self.tableView. Andernfalls erhalten Sie unerwartete Ergebnisse, wenn der searchDisplayController aktiv ist, insbesondere wenn die Suchergebnisse gescrollt werden.

Beispielsweise:

- (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];
}
Zaggo
quelle
2
  1. Definieren Sie ein Makro für Tags von Schaltflächen:

    #define AccessoryViewTagSinceValue 100000 // (AccessoryViewTagSinceValue * sections + rows) must be LE NSIntegerMax
  2. Erstellen Sie die Schaltfläche und legen Sie beim Erstellen einer Zelle die cell.accessoryView fest

    UIButton *accessoryButton = [UIButton buttonWithType:UIButtonTypeContactAdd];
    accessoryButton.frame = CGRectMake(0, 0, 30, 30);
    [accessoryButton addTarget:self action:@selector(accessoryButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
    cell.accessoryView = accessoryButton;
  3. Legen Sie cell.accessoryView.tag von indexPath in der UITableViewDataSource-Methode -tableView: cellForRowAtIndexPath fest:

    cell.accessoryView.tag = indexPath.section * AccessoryViewTagSinceValue + indexPath.row;
  4. Ereignishandler für Schaltflächen

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

    - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
        // do sth.
    }
Herr Ming
quelle
1
Niemand sollte verwenden, es tagsei denn, wenn dies unbedingt erforderlich ist, suchen Sie nach einer anderen Lösung.
Lifely
2

Wenn Sie auf die Schaltfläche tippen, kann die folgende Methode in einer UITableViewCell-Unterklasse aufgerufen werden

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

 }
Eric Welander
quelle
1

Mit yanchenko Ansatz musste ich hinzufügen: [accBtn setFrame:CGRectMake(0, 0, 20, 20)];

Wenn Sie die xib-Datei zum Anpassen Ihrer tableCell verwenden, wird initWithStyle: reuseIdentifier: nicht aufgerufen.

Überschreiben Sie stattdessen:

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

[super awakeFromNib];

}
Toydor
quelle
1

Sie müssen a verwenden UIControl, um den Ereignisversand (z. B. a UIButton) ordnungsgemäß zu erhalten, anstatt einfach UIView/UIImageView.

Ikarius
quelle
1

Swift 5

Bei diesem Ansatz wird der UIButton.tagindexPath mithilfe der grundlegenden Bitverschiebung gespeichert. Der Ansatz funktioniert auf 32- und 64-Bit-Systemen, solange Sie nicht mehr als 65535 Abschnitte oder Zeilen haben.

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
}
Brody Robertson
quelle
0

Ab iOS 3.2 können Sie die von anderen hier empfohlenen Schaltflächen vermeiden und stattdessen Ihr UIImageView mit einer Tippgestenerkennung verwenden. Stellen Sie sicher, dass die Benutzerinteraktion aktiviert ist, die in UIImageViews standardmäßig deaktiviert ist.

aeu
quelle