Tekan lama pada UITableView

191

Saya ingin menangani pers yang panjang pada a UITableViewCell untuk mencetak "menu akses cepat". Apakah seseorang sudah melakukan ini?

Terutama gerakan yang dikenali UITableView?

untuk
sumber

Jawaban:

427

Pertama-tama tambahkan pengenal isyarat pers yang panjang ke tampilan tabel:

UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc] 
  initWithTarget:self action:@selector(handleLongPress:)];
lpgr.minimumPressDuration = 2.0; //seconds
lpgr.delegate = self;
[self.myTableView addGestureRecognizer:lpgr];
[lpgr release];

Kemudian pada penangan isyarat:

-(void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    CGPoint p = [gestureRecognizer locationInView:self.myTableView];

    NSIndexPath *indexPath = [self.myTableView indexPathForRowAtPoint:p];
    if (indexPath == nil) {
        NSLog(@"long press on table view but not on a row");
    } else if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
        NSLog(@"long press on table view at row %ld", indexPath.row);
    } else {
        NSLog(@"gestureRecognizer.state = %ld", gestureRecognizer.state);
    }
}

Anda harus berhati-hati dengan ini sehingga tidak mengganggu ketukan normal pengguna sel dan juga mencatat yang handleLongPressmungkin menyala beberapa kali (ini akan disebabkan oleh perubahan status pengenalan isyarat).

CDspace
sumber
1
Luar Biasa !!! Terima kasih banyak! Tapi pertanyaan kecil terakhir: Mengapa metode handleLongPress adalah panggilan ketika sentuhan berakhir ???
untuk
111
Koreksi: menyala beberapa kali untuk menunjukkan berbagai kondisi gerakan (mulai, diubah, berakhir, dll). Jadi dalam metode handler, periksa properti state dari gesture diakui untuk menghindari melakukan tindakan di setiap keadaan gesture. Misalnya: if (gestureRecognizer.state == UIGestureRecognizerStateBegan) ....
3
Jangan lupa, pengenal isyarat sekarang dapat ditambahkan ke elemen UI langsung di Interface Builder dan terhubung melalui metode IBAction, jadi jawaban ini bahkan lebih mudah ;-) (ingat untuk melampirkan pengenal ke UITableView, bukan UITableViewCell...)
10
Juga konfirmasi ke protokol UIGestureRecognizerDelegate di file class.h
Vaquita
1
Bagaimana Anda mencegah tampilan tabel dari menavigasi ke dalam sel (jika Anda telah menerapkan 'didSelectRowAtIndexPath')
Marchy
46

Saya telah menggunakan jawaban Anna-Karenina, dan itu bekerja hampir hebat dengan bug yang sangat serius.

Jika Anda menggunakan bagian, menekan lama judul bagian akan memberi Anda hasil yang salah dengan menekan baris pertama pada bagian itu, saya telah menambahkan versi tetap di bawah ini (termasuk pemfilteran panggilan tiruan berdasarkan status gerakan, per Saran Anna-Karenina).

- (IBAction)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {

        CGPoint p = [gestureRecognizer locationInView:self.tableView];

        NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
        if (indexPath == nil) {
            NSLog(@"long press on table view but not on a row");
        } else {
            UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
            if (cell.isHighlighted) {
                NSLog(@"long press on table view at section %d row %d", indexPath.section, indexPath.row);
            }
        }
    }
}
marmor
sumber
Hai @marmor: Saya ingin bertanya apakah mungkin untuk mengidentifikasi hanya sebagian dari tampilan yang disentuh pengguna?
Manthan
Hei, Anda dapat menggunakan hitTest untuk itu ( developer.apple.com/library/ios/documentation/uikit/reference/… ). Lihat jawaban ini misalnya tentang cara menggunakan: stackoverflow.com/a/2793253/819355
marmor
Sementara jawaban yang diterima bekerja. Itu log beberapa kebakaran, bersama dengan penebangan sambil menyeret di layar. Jawaban ini tidak. Salin dan tempel jawaban yang sah. Terima kasih.
ChrisOSX
29

Answer in Swift 5 (Lanjutan dari jawaban Ricky di Swift)

Tambahkan UIGestureRecognizerDelegateke ViewController Anda

 override func viewDidLoad() {
    super.viewDidLoad()

    //Long Press
    let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
    longPressGesture.minimumPressDuration = 0.5
    self.tableView.addGestureRecognizer(longPressGesture)
 }

Dan fungsinya:

@objc func handleLongPress(longPressGesture: UILongPressGestureRecognizer) {
    let p = longPressGesture.location(in: self.tableView)
    let indexPath = self.tableView.indexPathForRow(at: p)
    if indexPath == nil {
        print("Long press on table view, not row.")
    } else if longPressGesture.state == UIGestureRecognizer.State.began {
        print("Long press on row, at \(indexPath!.row)")
    }
}
Ben
sumber
20

Ini adalah instruksi yang diklarifikasi yang menggabungkan jawaban Dawn Song dan jawaban Marmor.

Seret Recognizer Gerakan Tekan lama dan letakkan di Table Cell Anda. Ini akan melompat ke bagian bawah daftar di sebelah kiri.

masukkan deskripsi gambar di sini

Kemudian hubungkan pengenal gesture dengan cara yang sama seperti Anda menghubungkan tombol. masukkan deskripsi gambar di sini

Tambahkan kode dari Marmor di penangan tindakan

- (IBAction)handleLongPress:(UILongPressGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateBegan) {

    CGPoint p = [sender locationInView:self.tableView];

    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:p];
    if (indexPath == nil) {
        NSLog(@"long press on table view but not on a row");
    } else {
        UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
        if (cell.isHighlighted) {
            NSLog(@"long press on table view at section %d row %d", indexPath.section, indexPath.row);
        }
    }
}

}

Ryan Heitner
sumber
2
Jawaban terbaik menurut saya
Asen Kasimov
8
Long Press Gesture Recognizer harus diterapkan pada tampilan tabel, bukan sel tampilan tabel. Menjatuhkannya ke sel tampilan tabel hanya akan memiliki baris 0 mendengarkan tekan lama.
Alex
14

Tampaknya lebih efisien untuk menambahkan pengenal langsung ke sel seperti yang ditunjukkan di sini:

Ketuk & Tahan untuk Sel TableView, Lalu dan Sekarang

(gulir ke contoh di bagian bawah)

JR
sumber
7
Bagaimana cara mengalokasikan objek pengenal isyarat baru untuk setiap baris lebih efisien daripada pengenal tunggal untuk seluruh tabel?
user2393462435
7
Ingat meskipun hanya ada beberapa sel yang dibuat jika dequeue bekerja dengan benar.
Semut
11

Jawab di Swift:

Tambahkan delegasi UIGestureRecognizerDelegate ke UITableViewController Anda.

Dalam UITableViewController:

override func viewDidLoad() {
    super.viewDidLoad()

    let longPressGesture:UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handleLongPress:")
    longPressGesture.minimumPressDuration = 1.0 // 1 second press
    longPressGesture.delegate = self
    self.tableView.addGestureRecognizer(longPressGesture)

}

Dan fungsinya:

func handleLongPress(longPressGesture:UILongPressGestureRecognizer) {

    let p = longPressGesture.locationInView(self.tableView)
    let indexPath = self.tableView.indexPathForRowAtPoint(p)

    if indexPath == nil {
        print("Long press on table view, not row.")
    }
    else if (longPressGesture.state == UIGestureRecognizerState.Began) {
        print("Long press on row, at \(indexPath!.row)")
    }

}
Ricky
sumber
6

Saya mengumpulkan sedikit kategori tentang UITableView berdasarkan jawaban Anna Karenina yang luar biasa.

Seperti ini, Anda akan memiliki metode delegasi yang nyaman seperti yang biasa Anda lakukan ketika berhadapan dengan tampilan tabel biasa. Saksikan berikut ini:

//  UITableView+LongPress.h

#import <UIKit/UIKit.h>

@protocol UITableViewDelegateLongPress;

@interface UITableView (LongPress) <UIGestureRecognizerDelegate>
@property(nonatomic,assign)   id <UITableViewDelegateLongPress>   delegate;
- (void)addLongPressRecognizer;
@end


@protocol UITableViewDelegateLongPress <UITableViewDelegate>
- (void)tableView:(UITableView *)tableView didRecognizeLongPressOnRowAtIndexPath:(NSIndexPath *)indexPath;
@end



//  UITableView+LongPress.m

#import "UITableView+LongPress.h"

@implementation UITableView (LongPress)
@dynamic delegate;

- (void)addLongPressRecognizer {
    UILongPressGestureRecognizer *lpgr = [[UILongPressGestureRecognizer alloc]
                                          initWithTarget:self action:@selector(handleLongPress:)];
    lpgr.minimumPressDuration = 1.2; //seconds
    lpgr.delegate = self;
    [self addGestureRecognizer:lpgr];
}


- (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer
{
    CGPoint p = [gestureRecognizer locationInView:self];

    NSIndexPath *indexPath = [self indexPathForRowAtPoint:p];
    if (indexPath == nil) {
        NSLog(@"long press on table view but not on a row");
    }
    else {
        if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
            // I am not sure why I need to cast here. But it seems to be alright.
            [(id<UITableViewDelegateLongPress>)self.delegate tableView:self didRecognizeLongPressOnRowAtIndexPath:indexPath];
        }
    }
}

Jika Anda ingin menggunakan ini dalam UITableViewController, Anda mungkin perlu subkelas dan menyesuaikan dengan protokol baru.

Ini sangat bagus untuk saya, semoga membantu orang lain!

de.
sumber
Penggunaan pola delegasi dan kategori yang luar biasa
valeCocoa
5

Swift 3 menjawab, menggunakan sintaksis modern, memasukkan jawaban lain, dan menghilangkan kode yang tidak dibutuhkan.

override func viewDidLoad() {
    super.viewDidLoad()
    let recognizer = UILongPressGestureRecognizer(target: self, action: #selector(tablePressed))
    tableView.addGestureRecognizer(recognizer)
 }

@IBAction func tablePressed(_ recognizer: UILongPressGestureRecognizer) {
    let point = recognizer.location(in: tableView)

    guard recognizer.state == .began,
          let indexPath = tableView.indexPathForRow(at: point),
          let cell = tableView.cellForRow(at: indexPath),
          cell.isHighlighted
    else {
        return
    }

    // TODO
}
Phatmann
sumber
2

Cukup tambahkan UILongPressGestureRecognizer ke sel prototipe yang diberikan di storyboard, lalu tarik gerakan ke file .m viewController's .m untuk membuat metode tindakan. Saya membuatnya seperti yang saya katakan.

DawnSong
sumber
Bisakah Anda jelaskan lebih banyak? Apakah Anda membuat sel prototipe sebagai properti di VC Anda?
Ethan Parker
-2

Gunakan properti cap waktu UITouch dalam sentuhan. Mulai untuk meluncurkan timer atau menghentikannya ketika disentuh.

Thomas Joulin
sumber
Terima kasih atas jawaban Anda, tetapi bagaimana saya bisa mendeteksi baris mana yang terkait dengan sentuhan?
foOg
Saya mungkin salah, tetapi tidak ada yang disediakan untuk membantu Anda melakukan itu. Anda harus mendapatkan indeks sel yang terlihat saat ini dengan [tableView indexPathsForVisibleRows] dan kemudian, dengan menggunakan beberapa perhitungan (tableView Anda mengimbangi dari atas + X dikalikan baris) Anda akan tahu bahwa koordinat jari Anda adalah di mana baris.
Thomas Joulin
Saya yakin ada cara yang lebih mudah untuk melakukan itu, toh jika Anda punya ide lain, saya akan ada di sini :)
foOg
Saya akan senang mengetahui juga jika sesuatu yang lebih mudah adalah mungkin. Tapi saya tidak berpikir ada, sebagian besar karena itu bukan cara Apple ingin kita menangani interaksi ... Sepertinya cara berpikir Android ini "menu akses cepat". Jika itu aplikasi saya, saya akan menanganinya seperti aplikasi Twitter. Sapuan ke kiri menampilkan opsi
Thomas Joulin
Ya, saya memikirkannya, jadi jika saya benar-benar tidak bisa melakukannya dengan acara pers yang lama, saya akan menggunakan metode swipe. Tapi, mungkin seseorang dalam stack-overflow melakukannya ...
untuk