Menggunakan gambar khusus untuk aksesoriView UITableViewCell dan membuatnya merespons ke UITableViewDelegate

139

Saya menggunakan UITableViewCell yang dibuat khusus, termasuk yang sama untuk sel accessoryView. Pengaturan saya untuk accessoryView terjadi dengan cara seperti ini:

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

Juga ketika sel diinisialisasi, menggunakan initWithFrame:reuseIdentifier:I memastikan untuk mengatur properti berikut:

self.userInteractionEnabled = YES;

Sayangnya di UITableViewDelegate saya, tableView:accessoryButtonTappedForRowWithIndexPath:metode saya (coba ulangi sebanyak 10 kali) tidak terpicu. Delegasi pasti terhubung dengan benar.

Apa yang mungkin hilang?

Terima kasih semuanya.


sumber

Jawaban:

228

Sayangnya metode itu tidak dipanggil kecuali tipe tombol internal yang disediakan saat Anda menggunakan salah satu tipe yang telah ditentukan diketuk. Untuk menggunakan milik Anda sendiri, Anda harus membuat aksesori sebagai tombol atau subkelas UIControl lainnya (saya akan merekomendasikan tombol menggunakan -buttonWithType:UIButtonTypeCustomdan mengatur gambar tombol, daripada menggunakan UIImageView).

Berikut adalah beberapa hal yang saya gunakan dalam Outpost, yang mengkustomisasi cukup dari widget standar (hanya sedikit, untuk mencocokkan pewarnaan teal kami) yang akhirnya saya lakukan dengan menggunakan subclass perantara UITableViewController saya sendiri untuk menyimpan kode utilitas untuk semua tampilan tabel lain untuk digunakan (mereka sekarang subklas OPTableViewController).

Pertama, fungsi ini mengembalikan tombol pengungkapan detail baru menggunakan grafik khusus kami:

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

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

    return ( button );
}

Tombol akan memanggil rutin ini setelah selesai, yang kemudian mengumpankan rutin UITableViewDelegate standar untuk tombol aksesori:

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

Fungsi ini menempatkan baris dengan mendapatkan lokasi dalam tampilan tabel dari sentuhan dari acara yang disediakan oleh tombol dan meminta tampilan tabel untuk jalur indeks baris pada saat itu.

Jim Dovey
sumber
Terima kasih, Jim. Sayang sekali saya menghabiskan lebih dari 20 menit bertanya-tanya mengapa saya tidak bisa melakukannya dengan imageView kustom. Saya baru saja melihat cara melakukan ini pada contoh aplikasi Aksesori Apple. Jawaban Anda dijelaskan dan didokumentasikan dengan baik jadi saya menandainya dan menyimpannya. Terima kasih lagi. :-)
Jim, jawaban yang bagus. Satu masalah potensial (setidaknya pada akhir saya) - Saya harus menambahkan baris berikut untuk mendapatkan sentuhan untuk mendaftar pada tombol: button.userInteractionEnabled = YA;
Mike Laurence
11
Hanya untuk orang lain yang melihat jawaban ini, Anda juga bisa menaruh tag pada tombol yang sesuai dengan baris (jika Anda memiliki beberapa bagian, Anda perlu melakukan beberapa perhitungan) dan kemudian cukup tarik tag keluar dari tombol di fungsi. Saya pikir ini mungkin sedikit lebih cepat daripada menghitung sentuhan.
RyanJM
3
ini mengharuskan Anda untuk membuat hard-code self.tableView. bagaimana jika Anda tidak tahu tampilan tabel mana yang berisi baris?
user102008
4
@RyanJM Saya dulu berpikir bahwa melakukan hitTest adalah berlebihan dan tag sudah cukup. Saya sebenarnya telah menggunakan ide tag di beberapa kode saya. Tapi hari ini saya menemui masalah di mana pengguna dapat menambahkan baris baru. Ini membunuh hack menggunakan tag. Solusi yang disarankan oleh Jim Dovey (dan seperti yang terlihat dalam kode sampel Apple) adalah solusi umum dan berfungsi dalam semua situasi
srik
77

Saya menemukan situs web ini sangat membantu: tampilan aksesori khusus untuk uitableview Anda di iphone

Singkatnya, gunakan ini di 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;

kemudian, terapkan metode ini:

- (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
sumber
4
Saya akan mengatakan +1 untuk ini karena apa yang direkomendasikan Apple lakukan dalam kode sampel mereka dalam dokumen mereka: developer.apple.com/library/ios/#samplecode/Accessory/Listings/…
cleverbit
Mengatur frame adalah bagian yang hilang untuk saya. Anda juga dapat mengaturImage (bukan latar belakang) selama Anda tidak menginginkan teks apa pun.
Jeremy Hicks
1
Tautan tersebut rusak dalam jawaban @ richarddas. Tautan Baru: developer.apple.com/library/prerelease/ios/samplecode/Accessory/…
delavega66
7

Pendekatan saya adalah membuat UITableViewCellsubclass dan merangkum logika yang akan memanggil metode biasa UITableViewDelegatedi dalamnya.

// 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
sumber
Ini adalah jawaban TERBAIK. Tapi button.superview, cell.superviewdan [tableView.delegate tableView:...]tidak cukup aman.
Tuan Ming,
3

Perpanjangan jawaban Jim Dovey di atas:

Hati-hati saat Anda menggunakan UISearchBarController dengan UITableView Anda. Dalam hal ini Anda ingin memeriksa self.searchDisplayController.activedan menggunakan self.searchDisplayController.searchResultsTableViewalih-alih self.tableView. Kalau tidak, Anda akan mendapatkan hasil yang tidak terduga ketika searchDisplayController aktif, terutama ketika hasil pencarian digulir.

Sebagai contoh:

- (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
sumber
2
  1. Tetapkan makro untuk tag tombol:

    #define AccessoryViewTagSinceValue 100000 // (AccessoryViewTagSinceValue * sections + rows) must be LE NSIntegerMax
  2. Buat tombol dan atur cell.accessoryView saat membuat sel

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

    cell.accessoryView.tag = indexPath.section * AccessoryViewTagSinceValue + indexPath.row;
  4. Penangan acara untuk tombol

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

    - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
        // do sth.
    }
Tuan Ming
sumber
1
Tidak seorang pun boleh menggunakan tagkecuali jika mutlak diperlukan, mencari solusi lain.
Seumur hidup
2

Ketika tombol diketuk, Anda dapat memanggil metode berikut di dalam subkelas 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]];

 }
Eric Welander
sumber
1

Dengan pendekatan yanchenko saya harus menambahkan: [accBtn setFrame:CGRectMake(0, 0, 20, 20)];

Jika Anda menggunakan file xib untuk menyesuaikan tableCell Anda, maka initWithStyle: reuseIdentifier: tidak akan dipanggil.

Alih-alih menimpa:

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

[super awakeFromNib];

}
Toydor
sumber
1

Anda harus menggunakan a UIControluntuk mendapatkan pengiriman acara dengan benar (misalnya a UIButton) alih-alih sederhana UIView/UIImageView.

ikarius
sumber
1

Cepat 5

Pendekatan ini menggunakan UIButton.taguntuk menyimpan indexPath menggunakan bit-shifting dasar. Pendekatan ini akan bekerja pada sistem 32 & 64 bit selama Anda tidak memiliki lebih dari 65535 bagian atau baris.

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
sumber
0

Pada iOS 3.2 Anda dapat menghindari tombol yang direkomendasikan oleh orang lain di sini dan sebagai gantinya menggunakan UIImageView Anda dengan pengenal isyarat ketukan. Pastikan untuk mengaktifkan interaksi pengguna, yang dinonaktifkan secara default di UIImageViews.

aeu
sumber