Gesek ke Hapus dan tombol "Lainnya" (seperti di aplikasi Mail di iOS 7)

246

Cara membuat tombol "lebih" ketika pengguna menggesek sel dalam tampilan tabel (seperti aplikasi email di ios 7)

Saya telah mencari informasi ini baik di sini maupun di forum Cocoa Touch, tetapi sepertinya saya tidak dapat menemukan jawabannya dan saya berharap seseorang yang lebih pintar daripada saya dapat memberi saya solusi.

Saya ingin bahwa ketika pengguna menggesek sel tampilan tabel, untuk menampilkan lebih dari satu tombol pengeditan (dia default adalah tombol hapus). Di aplikasi Mail untuk iOS 7 Anda dapat menggesek untuk menghapus, tetapi ada tombol "LEBIH" yang muncul.

masukkan deskripsi gambar di sini

Guy Kahlon
sumber
Untuk menambahkan tombol "Hapus", saya menerapkan dua fungsi berikut. - (BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath; - (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editingStyle untukRowAtIndexPath: (NSIndexPath *) indexPath; Dan saya ingin menambahkan tombol "Lainnya" di sebelahnya.
Guy Kahlon
3
@MonishBansal Bansal Sepertinya seseorang di utas ini ( devforums.apple.com/message/860459#860459 di forum pengembang Apple) maju terus dan membangun implementasi mereka sendiri. Anda dapat menemukan proyek yang melakukan apa yang Anda inginkan di GitHub: github.com/daria-kopaliani/DAContextMenuTableViewController
Guy Kahlon
8
@GuyKahlonMatrix terima kasih atas solusinya bekerja seperti pesona. Pertanyaan ini adalah hasil No.1 di banyak pencarian google, dan orang-orang dipaksa untuk bertukar pengetahuan mereka menggunakan komentar karena beberapa pria memutuskan akan lebih membantu untuk menutup pertanyaan dan mengajarkan demokrasi. Tempat ini jelas membutuhkan mod yang lebih baik.
Şafak Gezer
2
Jika Anda dapat menargetkan iOS 8, jawaban saya di bawah ini adalah yang Anda inginkan.
Johnny

Jawaban:

126

Cara Melaksanakan

Sepertinya iOS 8 membuka API ini. Petunjuk tentang fungsi tersebut ada dalam Beta 2.

Untuk membuat sesuatu berfungsi, terapkan dua metode berikut pada delegasi UITableView Anda untuk mendapatkan efek yang diinginkan (lihat intisari contohnya).

- tableView:editActionsForRowAtIndexPath:
- tableView:commitEditingStyle:forRowAtIndexPath:


Masalah Dikenal

Dokumentasi mengatakan tableView: commitEditingStyle: forRowAtIndexPath adalah:

"Tidak dipanggil untuk tindakan edit menggunakan UITableViewRowAction - pengendali tindakan akan dipanggil sebagai gantinya."

Namun, menggesekkan tidak akan berhasil tanpanya. Bahkan jika metode rintisan kosong, masih membutuhkannya, untuk saat ini. Ini jelas merupakan bug dalam versi beta 2.


Sumber

https://twitter.com/marksands/status/481642991745265664 https://gist.github.com/marksands/76558707f583dbb8f870

Jawaban Asli: https://stackoverflow.com/a/24540538/870028


Memperbarui:

Contoh kode dengan ini berfungsi (Dalam Swift): http://dropbox.com/s/0fvxosft2mq2v5m/DeleteRowExampleSwift.zip

Kode sampel berisi metode yang mudah diikuti di MasterViewController.swift, dan hanya dengan metode ini Anda mendapatkan perilaku yang ditunjukkan dalam tangkapan layar OP:

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {

    var moreRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "More", handler:{action, indexpath in
        println("MORE•ACTION");
    });
    moreRowAction.backgroundColor = UIColor(red: 0.298, green: 0.851, blue: 0.3922, alpha: 1.0);

    var deleteRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Delete", handler:{action, indexpath in
        println("DELETE•ACTION");
    });

    return [deleteRowAction, moreRowAction];
}
Johnny
sumber
1
Ini tampaknya benar, tetapi dalam Xcode 6 GM gerakan gesek tampaknya tidak berfungsi. EditActions masih dapat diakses dengan menempatkan tampilan tabel dalam mode pengeditan. Adakah yang menemukan bahwa gesek tidak berfungsi?
Siegfoult
@Siegfoult Sudahkah Anda mencoba mengimplementasikan (walaupun dibiarkan kosong) tableView: commitEditingStyle: forRowAtIndexPath :?
Johnny
Saya belum bekerja di objektif c .. Kode yang sama saya tulis. tolong sarankan beberapa petunjuk.
Solid Soft
@ SolidSoft Apakah Anda memiliki contoh proyek yang dapat saya lihat? Saya mungkin bisa membantu lebih baik dengan cara itu.
Johnny
3
Untuk menjawab komentar saya sendiri. Anda menelepon tableView.editing = false( NOdalam objc) dan sel akan "menutup".
Ben Lachman
121

Saya telah membuat perpustakaan baru untuk menerapkan tombol swippable yang mendukung berbagai transisi dan tombol yang dapat diperluas seperti aplikasi email iOS 8.

https://github.com/MortimerGoro/MGSwipeTableCell

Pustaka ini kompatibel dengan semua cara berbeda untuk membuat UITableViewCell dan diuji pada iOS 5, iOS 6, iOS 7 dan iOS 8.

Berikut contoh beberapa transisi:

Transisi perbatasan:

Transisi perbatasan

Transisi klip

Transisi klip

Transisi 3D:

masukkan deskripsi gambar di sini

MortimerGoro
sumber
1
Kerja bagus! Akan luar biasa memiliki callback untuk menyesuaikan animasi.
Pacu
1
@MortimerGoro Pekerja yang baik. Itu terlihat bagus. Saya mencoba menerapkan efek serupa di salah satu Proyek Android saya. Tolong beritahu saya bagaimana saya bisa mencapai ini di Android?
Nitesh Kumar
di iOS 8 + iPad, saya hanya tidak bisa melakukan swipe.
ScorpionKing2k5
Ini adalah perpustakaan yang luar biasa dan yang sangat bagus adalah masih memiliki dukungan.
confile
@MortimerGoro, saya mencoba dengan kerangka kerja "MGSwipeTableCel" tetapi masalahnya adalah ketika saya memuat ulang meja saya kemudian tombol geser disembunyikan. Apa saja yang ada untuk masalah ini.
Ganesh Guturi
71

Jawaban Johnny adalah jawaban yang tepat. Saya hanya menambahkan ini di bawah di objektif-c untuk membuatnya lebih jelas untuk pemula (dan kita yang menolak untuk belajar sintaks Swift :)

Pastikan Anda mendeklarasikan delegasi uitableview dan memiliki metode berikut:

 -(NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
 UITableViewRowAction *button = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 1" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
    {
        NSLog(@"Action to perform with Button 1");
    }];
    button.backgroundColor = [UIColor greenColor]; //arbitrary color
    UITableViewRowAction *button2 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 2" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
                                    {
                                        NSLog(@"Action to perform with Button2!");
                                    }];
    button2.backgroundColor = [UIColor blueColor]; //arbitrary color

    return @[button, button2]; //array with all the buttons you want. 1,2,3, etc...
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// you need to implement this method too or nothing will work:

}
 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return YES; //tableview must be editable or nothing will work...
    }
timothykc
sumber
1
Penting untuk menyebutkan canEditRowAtIndexPath
Heckscheibe
Jika saya memuat ulang tabel setelah menggesek sel, maka tombol gesek itu terlihat atau disembunyikan?
Ganesh Guturi
25

Ini (agak konyol) API pribadi.

Dua metode berikut ini bersifat pribadi dan dikirim ke delegasi UITableView:

-(NSString *)tableView:(UITableView *)tableView titleForSwipeAccessoryButtonForRowAtIndexPath:(NSIndexPath *)indexPath;
-(void)tableView:(UITableView *)tableView swipeAccessoryButtonPushedForRowAtIndexPath:(NSIndexPath *)indexPath;

Mereka cukup jelas.

Jonathan.
sumber
4
Apple telah membuka fitur ini dengan iOS 8. Lihat jawaban Johnny di bawah ini.
Siegfoult
24

Untuk meningkatkan jawaban Johnny, ini sekarang dapat dilakukan dengan menggunakan API publik sebagai berikut:

func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {

    let moreRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "More", handler:{action, indexpath in
        print("MORE•ACTION");
    });
    moreRowAction.backgroundColor = UIColor(red: 0.298, green: 0.851, blue: 0.3922, alpha: 1.0);

    let deleteRowAction = UITableViewRowAction(style: UITableViewRowActionStyle.default, title: "Delete", handler:{action, indexpath in
        print("DELETE•ACTION");
    });

    return [deleteRowAction, moreRowAction];
}
Arunabh Das
sumber
17

Saya harap Anda tidak bisa menunggu sampai apel memberi Anda apa yang Anda butuhkan, kan? Jadi inilah pilihan saya.

Buat sel khusus. Memiliki dua uiviews di dalamnya

1. upper
2. lower

Pada tampilan yang lebih rendah, tambahkan tombol apa saja yang Anda butuhkan. Atasi tindakannya sama seperti IBActions lainnya. Anda dapat menentukan waktu animasi, gaya dan apa pun.

sekarang tambahkan uiswipegesture ke tampilan atas dan ungkapkan tampilan bawah Anda pada gerakan swipe. Saya telah melakukan ini sebelumnya dan ini pilihan paling sederhana sejauh yang saya ketahui.

Semoga bantuan itu.

Deepukjayan
sumber
7

Ini tidak mungkin menggunakan SDK standar. Namun ada berbagai solusi pihak ketiga yang sedikit banyak meniru perilaku di Mail.app. Beberapa dari mereka (mis. MCSwipeTableViewCell , DAContextMenuTableViewController , RMSwipeTableViewCell ) mendeteksi gesekan menggunakan pengenal isyarat, beberapa dari mereka (misalnya SWTableViewCell ) meletakkan UISScrollView kedua di bawah standar UITableViewCellScrollView(subview pribadi dari UITableViewCell) dan beberapa dari mereka memodifikasi perilaku dariUITableViewCellScrollView .

Saya paling suka pendekatan terakhir karena penanganan sentuhan terasa paling alami. Secara khusus, MSCMoreOptionTableViewCell baik. Pilihan Anda dapat bervariasi tergantung pada kebutuhan spesifik Anda (apakah Anda memerlukan panci dari kiri ke kanan juga, apakah Anda memerlukan kompatibilitas iOS 6, dll.). Perlu diketahui juga bahwa sebagian besar pendekatan ini membawa beban: mereka dapat dengan mudah merusak versi iOS di masa depan jika Apple membuat perubahan dalam UITableViewCellhierarki subview.

Ortwin Gentz
sumber
7

Kode versi Swift 3 tanpa menggunakan perpustakaan apa pun:

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        tableView.tableFooterView = UIView(frame: CGRect.zero) //Hiding blank cells.
        tableView.separatorInset = UIEdgeInsets.zero
        tableView.dataSource = self
        tableView.delegate = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return 4
    }

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

        let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "tableCell", for: indexPath)

        return cell
    }

    //Enable cell editing methods.
    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {

        return true
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {

    }

    func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {

        let more = UITableViewRowAction(style: .normal, title: "More") { action, index in
            //self.isEditing = false
            print("more button tapped")
        }
        more.backgroundColor = UIColor.lightGray

        let favorite = UITableViewRowAction(style: .normal, title: "Favorite") { action, index in
            //self.isEditing = false
            print("favorite button tapped")
        }
        favorite.backgroundColor = UIColor.orange

        let share = UITableViewRowAction(style: .normal, title: "Share") { action, index in
            //self.isEditing = false
            print("share button tapped")
        }
        share.backgroundColor = UIColor.blue

        return [share, favorite, more]
    }

}
mriaz0011
sumber
6

Anda perlu subkelas UITableViewCell metode subclass dan subclass willTransitionToState:(UITableViewCellStateMask)stateyang dipanggil setiap kali pengguna menggesek sel. Itustate bendera akan memberitahu Anda jika tombol Delete menunjukkan, dan menampilkan / menyembunyikan tombol More Anda di sana.

Sayangnya metode ini tidak memberi Anda lebar tombol Delete atau waktu animasi. Jadi, Anda perlu mengamati & mengkode kode bingkai tombol More Anda dan waktu animasi ke dalam kode Anda (saya pribadi berpikir Apple perlu melakukan sesuatu tentang ini).

Khanh Nguyen
sumber
7
"Saya pribadi berpikir Apple perlu melakukan sesuatu tentang ini". Saya setuju. Apakah Anda sudah menulis laporan bug / permintaan fitur kepada mereka?
Tafkadasoh
4

Untuk pemrograman cepat

func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
  if editingStyle == UITableViewCellEditingStyle.Delete {
    deleteModelAt(indexPath.row)
    self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
  }
  else if editingStyle == UITableViewCellEditingStyle.Insert {
    println("insert editing action")
  }
}

func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]? {
  var archiveAction = UITableViewRowAction(style: .Default, title: "Archive",handler: { (action: UITableViewRowAction!, indexPath: NSIndexPath!) in
        // maybe show an action sheet with more options
        self.tableView.setEditing(false, animated: false)
      }
  )
  archiveAction.backgroundColor = UIColor.lightGrayColor()

  var deleteAction = UITableViewRowAction(style: .Normal, title: "Delete",
      handler: { (action: UITableViewRowAction!, indexPath: NSIndexPath!) in
        self.deleteModelAt(indexPath.row)
        self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic);
      }
  );
  deleteAction.backgroundColor = UIColor.redColor()

  return [deleteAction, archiveAction]
}

func deleteModelAt(index: Int) {
  //... delete logic for model
}
Michael Yagudaev
sumber
@bibscy Anda dipersilakan untuk menyarankan suntingan. Belum pernah menggunakan cepat dalam waktu yang lama jadi tidak yakin apa sintaks yang benar
Michael Yagudaev
3

INI BISA MEMBANTU ANDA KELUAR.

-(NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath {
 UITableViewRowAction *button = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 1" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
    {
        NSLog(@"Action to perform with Button 1");
    }];
    button.backgroundColor = [UIColor greenColor]; //arbitrary color
    UITableViewRowAction *button2 = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Button 2" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath)
                                    {
                                        NSLog(@"Action to perform with Button2!");
                                    }];
    button2.backgroundColor = [UIColor blueColor]; //arbitrary color

    return @[button, button2]; //array with all the buttons you want. 1,2,3, etc...
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
// you need to implement this method too or nothing will work:

}
 - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return YES; //tableview must be editable or nothing will work...
    }
santoshIOS
sumber
3

Saya mencari untuk menambahkan fungsionalitas yang sama ke aplikasi saya, dan setelah melalui begitu banyak tutorial yang berbeda ( raywenderlich menjadi solusi DIY terbaik), saya menemukan bahwa Apple memiliki UITableViewRowActionkelas sendiri , yang sangat berguna.

Anda harus mengubah metode boilerpoint Tableview untuk ini:

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [AnyObject]?  {
    // 1   
    var shareAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Share" , handler: { (action:UITableViewRowAction!, indexPath:NSIndexPath!) -> Void in
    // 2
    let shareMenu = UIAlertController(title: nil, message: "Share using", preferredStyle: .ActionSheet)

    let twitterAction = UIAlertAction(title: "Twitter", style: UIAlertActionStyle.Default, handler: nil)
    let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)

    shareMenu.addAction(twitterAction)
    shareMenu.addAction(cancelAction)


    self.presentViewController(shareMenu, animated: true, completion: nil)
    })
    // 3
    var rateAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Rate" , handler: { (action:UITableViewRowAction!, indexPath:NSIndexPath!) -> Void in
    // 4
    let rateMenu = UIAlertController(title: nil, message: "Rate this App", preferredStyle: .ActionSheet)

    let appRateAction = UIAlertAction(title: "Rate", style: UIAlertActionStyle.Default, handler: nil)
    let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)

    rateMenu.addAction(appRateAction)
    rateMenu.addAction(cancelAction)


    self.presentViewController(rateMenu, animated: true, completion: nil)
    })
    // 5
    return [shareAction,rateAction]
  }

Anda dapat mengetahui lebih lanjut tentang ini di Situs Ini . Dokumentasi Apple sendiri sangat berguna untuk mengubah warna latar belakang:

Warna latar belakang tombol aksi.

Deklarasi OBJECTIVE-C @property (nonatomic, copy) UIColor * backgroundColor Discussion Gunakan properti ini untuk menentukan warna latar belakang untuk tombol Anda. Jika Anda tidak menentukan nilai untuk properti ini, UIKit memberikan warna default berdasarkan nilai dalam properti style.

Ketersediaan Tersedia di iOS 8.0 dan yang lebih baru.

Jika Anda ingin mengubah font tombol, ini sedikit lebih rumit. Saya telah melihat pos lainnya di SO. Demi memberikan kode serta tautan, inilah kode yang mereka gunakan di sana. Anda harus mengubah tampilan tombol. Anda harus membuat referensi khusus ke tableviewcell, jika tidak Anda akan mengubah tampilan tombol di seluruh aplikasi Anda (saya tidak menginginkan itu, tetapi Anda mungkin, saya tidak tahu :))

Sasaran C:

+ (void)setupDeleteRowActionStyleForUserCell {

    UIFont *font = [UIFont fontWithName:@"AvenirNext-Regular" size:19];

    NSDictionary *attributes = @{NSFontAttributeName: font,
                      NSForegroundColorAttributeName: [UIColor whiteColor]};

    NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString: @"DELETE"
                                                                          attributes: attributes];

    /*
     * We include UIView in the containment hierarchy because there is another button in UserCell that is a direct descendant of UserCell that we don't want this to affect.
     */
    [[UIButton appearanceWhenContainedIn:[UIView class], [UserCell class], nil] setAttributedTitle: attributedTitle
                                                                                          forState: UIControlStateNormal];
}

Cepat:

    //create your attributes however you want to
    let attributes = [NSFontAttributeName: UIFont.systemFontOfSize(UIFont.systemFontSize())] as Dictionary!            

   //Add more view controller types in the []
    UIButton.appearanceWhenContainedInInstancesOfClasses([ViewController.self])

Ini adalah versi IMHO termudah, dan paling stream-lined. Semoga ini bisa membantu.

Pembaruan: Inilah versi Swift 3.0:

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    var shareAction:UITableViewRowAction = UITableViewRowAction(style: .default, title: "Share", handler: {(action, cellIndexpath) -> Void in
        let shareMenu = UIAlertController(title: nil, message: "Share using", preferredStyle: .actionSheet)

        let twitterAction = UIAlertAction(title: "Twitter", style: .default, handler: nil)
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)

        shareMenu.addAction(twitterAction)
        shareMenu.addAction(cancelAction)


        self.present(shareMenu,animated: true, completion: nil)
    })

    var rateAction:UITableViewRowAction = UITableViewRowAction(style: .default, title: "Rate" , handler: {(action, cellIndexpath) -> Void in
        // 4
        let rateMenu = UIAlertController(title: nil, message: "Rate this App", preferredStyle: .actionSheet)

        let appRateAction = UIAlertAction(title: "Rate", style: .default, handler: nil)
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)

        rateMenu.addAction(appRateAction)
        rateMenu.addAction(cancelAction)


        self.present(rateMenu, animated: true, completion: nil)
    })
    // 5
    return [shareAction,rateAction]
}
Septronic
sumber
1
Terima kasih atas jawaban Anda, saya yakin ini akan membantu banyak pengembang. Ya, Anda benar, sebenarnya Apple menyediakan solusi ini dari iOS 8. Namun sayangnya solusi asli ini tidak menyediakan fungsionalitas penuh. Misalnya, di aplikasi Mail Apple Anda memiliki tombol dari dua sisi (Satu tombol dari sisi kiri, dan tiga dari sisi kanan) dengan API saat ini dari Apple Anda tidak dapat menambahkan tombol ke kedua sisi, dan juga API saat ini tidak mendukung tindakan default saat pengguna menggesek panjang ke setiap sisi. Solusi terbaik untuk saat ini IMHO adalah MGSwipeTableCell sumber terbuka.
Guy Kahlon
@GuyKahlon ya, Anda memang benar dalam hal masalah geser ke kiri dan kanan, dan saya setuju bahwa untuk penyesuaian lebih lanjut, MGSwipeTableCell adalah yang terbaik. Apple sendiri bukanlah pilihan yang paling canggih, tetapi saya menemukannya paling mudah untuk tugas-tugas sederhana.
Septronic
@Strtronic Bisakah Anda memperbarui kode Anda ke Swift 3? shareMenu.tidak mengambil addActionmetode. Terima kasih
bibscy
@bibscy Saya telah menambahkan versi cepat. Apakah Anda memerlukan bit untuk atributnya juga? sharemenu hanyalah sebuah UIAlertController, jadi ia harus mengambil tindakan. Cobalah dan beri tahu saya jika ada keberuntungan :)
Septronic
3

Sebenarnya Swift 3 Jawaban

Ini adalah fungsi HANYA yang Anda butuhkan. Anda tidak memerlukan fungsi CanEdit atau CommitEditingStyle untuk tindakan kustom.

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
    let action1 = UITableViewRowAction(style: .default, title: "Action1", handler: {
        (action, indexPath) in
        print("Action1")
    })
    action1.backgroundColor = UIColor.lightGray
    let action2 = UITableViewRowAction(style: .default, title: "Action2", handler: {
        (action, indexPath) in
        print("Action2")
    })
    return [action1, action2]
}
William T.
sumber
3

Pada iOS 11 ini tersedia untuk umum di UITableViewDelegate. Berikut beberapa contoh kode:

- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {
    UIContextualAction *delete = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive
                                                                         title:@"DELETE"
                                                                       handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
                                                                           NSLog(@"index path of delete: %@", indexPath);
                                                                           completionHandler(YES);
                                                                       }];

    UIContextualAction *rename = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleNormal
                                                                         title:@"RENAME"
                                                                       handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) {
                                                                           NSLog(@"index path of rename: %@", indexPath);
                                                                           completionHandler(YES);
                                                                       }];

    UISwipeActionsConfiguration *swipeActionConfig = [UISwipeActionsConfiguration configurationWithActions:@[rename, delete]];
    swipeActionConfig.performsFirstActionWithFullSwipe = NO;

    return swipeActionConfig;
}

Juga tersedia:

- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView leadingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath;

Documents: https://developer.apple.com/documentation/uikit/uitableviewdelegate/2902367-tableview?language=objc

Lewis
sumber
3

Swift 4 & iOs 11+

@available(iOS 11.0, *)
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {

    let delete = UIContextualAction(style: .destructive, title: "Delete") { _, _, handler in

        handler(true)
        // handle deletion here
    }

    let more = UIContextualAction(style: .normal, title: "More") { _, _, handler in

        handler(true)
        // handle more here
    }

    return UISwipeActionsConfiguration(actions: [delete, more])
}
Andrey
sumber
2

Saya menggunakan tableViewCell untuk menampilkan banyak data, setelah menggesek () kanan ke kiri pada sel, ia akan menampilkan dua tombol Menyetujui dan menolak, ada dua metode, yang pertama adalah ApproveFunc yang mengambil satu argumen, dan yang lain adalah RejectFunc yang juga mengambil satu argumen.

masukkan deskripsi gambar di sini

func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
        let Approve = UITableViewRowAction(style: .normal, title: "Approve") { action, index in

            self.ApproveFunc(indexPath: indexPath)
        }
        Approve.backgroundColor = .green

        let Reject = UITableViewRowAction(style: .normal, title: "Reject") { action, index in

            self.rejectFunc(indexPath: indexPath)
        }
        Reject.backgroundColor = .red



        return [Reject, Approve]
    }

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    func ApproveFunc(indexPath: IndexPath) {
        print(indexPath.row)
    }
    func rejectFunc(indexPath: IndexPath) {
        print(indexPath.row)
    }
Akbar Khan
sumber
Bisakah Anda menambahkan beberapa penjelasan pada jawaban Anda sehingga pembaca dapat belajar darinya?
Nico Haase
Terima kasih atas cuplikan kode ini, yang mungkin memberikan bantuan terbatas dan segera. Sebuah penjelasan yang tepat akan sangat meningkatkan nilai jangka panjang dengan menunjukkan mengapa ini adalah solusi yang baik untuk masalah ini, dan akan membuatnya lebih bermanfaat untuk pembaca masa depan dengan lainnya, pertanyaan-pertanyaan serupa. Harap edit jawaban Anda untuk menambahkan beberapa penjelasan, termasuk asumsi yang Anda buat.
Tim Diekmann
1

Inilah cara yang agak rapuh untuk melakukannya yang tidak melibatkan API pribadi atau membangun sistem Anda sendiri. Anda melakukan hedging pada taruhan Anda bahwa Apple tidak melanggar ini dan semoga mereka akan merilis API yang dapat Anda ganti dengan beberapa baris kode ini.

  1. KVO self.contentView.superview.layer.sublayer. Lakukan ini di init. Ini adalah lapisan UIScrollView. Anda tidak dapat 'subview' KVO.
  2. Saat subview berubah, temukan tampilan konfirmasi penghapusan dalam scrollview.subviews. Ini dilakukan dalam callback mengamati.
  3. Gandakan ukuran tampilan itu dan tambahkan UIButton di sebelah kiri satu-satunya subview. Ini juga dilakukan dalam mengamati callback. Satu-satunya subview dari tampilan konfirmasi penghapusan adalah tombol hapus.
  4. (opsional) Acara UIButton harus mencari self.superview hingga menemukan UITableView dan kemudian memanggil sumber data atau metode delegasi yang Anda buat, seperti tableView: commitCustomEditingStyle: forRowAtIndexPath :. Anda dapat menemukan indexPath sel dengan menggunakan [tableView indexPathForCell: self].

Ini juga mengharuskan Anda menerapkan panggilan balik delegasi mengedit tampilan tabel tampilan standar.

static char kObserveContext = 0;

@implementation KZTableViewCell {
    UIScrollView *_contentScrollView;
    UIView *_confirmationView;
    UIButton *_editButton;
    UIButton *_deleteButton;
}

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        _contentScrollView = (id)self.contentView.superview;

        [_contentScrollView.layer addObserver:self
             forKeyPath:@"sublayers"
                options:0
                context:&kObserveContext];

        _editButton = [UIButton new];
        _editButton.backgroundColor = [UIColor lightGrayColor];
        [_editButton setTitle:@"Edit" forState:UIControlStateNormal];
        [_editButton addTarget:self
                        action:@selector(_editTap)
              forControlEvents:UIControlEventTouchUpInside];

    }
    return self;
}

-(void)dealloc {
    [_contentScrollView.layer removeObserver:self forKeyPath:@"sublayers" context:&kObserveContext];
}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if(context != &kObserveContext) {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        return;
    }
    if(object == _contentScrollView.layer) {
        for(UIView * view in _contentScrollView.subviews) {
            if([NSStringFromClass(view.class) hasSuffix:@"ConfirmationView"]) {
                _confirmationView = view;
                _deleteButton = [view.subviews objectAtIndex:0];
                CGRect frame = _confirmationView.frame;
                CGRect frame2 = frame;
                frame.origin.x -= frame.size.width;
                frame.size.width *= 2;
                _confirmationView.frame = frame;

                frame2.origin = CGPointZero;
                _editButton.frame = frame2;
                frame2.origin.x += frame2.size.width;
                _deleteButton.frame = frame2;
                [_confirmationView addSubview:_editButton];
                break;
            }
        }
        return;
    }
}

-(void)_editTap {
    UITableView *tv = (id)self.superview;
    while(tv && ![tv isKindOfClass:[UITableView class]]) {
        tv = (id)tv.superview;
    }
    id<UITableViewDelegate> delegate = tv.delegate;
    if([delegate respondsToSelector:@selector(tableView:editTappedForRowWithIndexPath:)]) {
        NSIndexPath *ip = [tv indexPathForCell:self];
        // define this in your own protocol
        [delegate tableView:tv editTappedForRowWithIndexPath:ip];
    }
}
@end
xtravar
sumber
Saya sangat senang jika Anda dapat memberikan kode sampel, Terima kasih
Guy Kahlon
Selesai Mungkin memiliki satu atau dua bug, tetapi Anda mendapatkan intinya.
xtravar
1

Ada perpustakaan luar biasa yang disebut SwipeCellKit, itu harus mendapatkan pengakuan lebih. Menurut saya itu lebih keren daripada MGSwipeTableCell. Yang terakhir ini tidak sepenuhnya mereplikasi perilaku sel aplikasi Mail sementara SwipeCellKititu. Silahkan lihat

Andrey Chernukha
sumber
Saya mencoba SwipeCellKitdan terkesan ... sampai saya mendapatkan salah satu dari Pengecualian karena jumlah baris sebelum pembaruan tampilan tabel tidak sama dengan setelah pembaruan +/- perubahan dalam baris. Masalahnya, saya tidak pernah mengubah set data saya. Jadi jika itu tidak mengkhawatirkan, saya tidak tahu apa itu. Jadi saya memutuskan untuk tidak menggunakannya dan hanya menggunakan metode UITableViewDelegate yang baru. Jika Anda membutuhkan lebih banyak penyesuaian, Anda selalu dapat mengesampingkanwillBeginEditingRowAt: ....
horseshoe7
@ horseshoe7 itu aneh. Saya tidak pernah mengalami pengecualian saat menggunakan SwipeCellKit. Lagi pula, hubungan seperti apa yang dimiliki sebuah sel terhadap pengecualian yang terjadi karena perubahan sumber data?
Andrey Chernukha
1

Cepat 4

func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
    let delete = UIContextualAction(style: .destructive, title: "Delete") { (action, sourceView, completionHandler) in
        print("index path of delete: \(indexPath)")
        completionHandler(true)
    }
    let rename = UIContextualAction(style: .normal, title: "Edit") { (action, sourceView, completionHandler) in
        print("index path of edit: \(indexPath)")
        completionHandler(true)
    }
    let swipeActionConfig = UISwipeActionsConfiguration(actions: [rename, delete])
    swipeActionConfig.performsFirstActionWithFullSwipe = false
    return swipeActionConfig
}
Aplikasi Vini
sumber
apa tampilan sumber dalam kode Anda? apakah itu Ikon atau Gambar?
Saeed Rahmatolahi
1
@SaeedRahmatolahi, sourceViewadalah "Tampilan tempat tindakan ditampilkan." Untuk info lebih lanjut, cari "UIContextualAction.Handler".
Mark Moeykens
0

Inilah satu solusi sederhana. Ia mampu menampilkan dan menyembunyikan UIView khusus di dalam UITableViewCell. Logika tampilan terdapat di dalam kelas yang diperluas dari UITableViewCell, BaseTableViewCell.

BaseTableViewCell.h

#import <UIKit/UIKit.h>

@interface BaseTableViewCell : UITableViewCell

@property(nonatomic,strong)UIView* customView;

-(void)showCustomView;

-(void)hideCustomView;

@end

BaseTableViewCell.M

#import "BaseTableViewCell.h"

@interface BaseTableViewCell()
{
    BOOL _isCustomViewVisible;
}

@end

@implementation BaseTableViewCell

- (void)awakeFromNib {
    // Initialization code
}

-(void)prepareForReuse
{
    self.customView = nil;
    _isCustomViewVisible = NO;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

-(void)showCustomView
{
    if(nil != self.customView)
    {
        if(!_isCustomViewVisible)
        {
            _isCustomViewVisible = YES;

            if(!self.customView.superview)
            {
                CGRect frame = self.customView.frame;
                frame.origin.x = self.contentView.frame.size.width;
                self.customView.frame = frame;
                [self.customView willMoveToSuperview:self.contentView];
                [self.contentView addSubview:self.customView];
                [self.customView didMoveToSuperview];
            }

            __weak BaseTableViewCell* blockSelf = self;
            [UIView animateWithDuration:.5 animations:^(){

                for(UIView* view in blockSelf.contentView.subviews)
                {
                    CGRect frame = view.frame;
                    frame.origin.x = frame.origin.x - blockSelf.customView.frame.size.width;
                    view.frame = frame;
                }
            }];
        }
    }
}

-(void)hideCustomView
{
    if(nil != self.customView)
    {
        if(_isCustomViewVisible)
        {
            __weak BaseTableViewCell* blockSelf = self;
            _isCustomViewVisible = NO;
            [UIView animateWithDuration:.5 animations:^(){
                for(UIView* view in blockSelf.contentView.subviews)
                {
                    CGRect frame = view.frame;
                    frame.origin.x = frame.origin.x + blockSelf.customView.frame.size.width;
                    view.frame = frame;
                }
            }];
        }
    }
}

@end

Untuk mendapatkan fungsionalitas ini, cukup rentangkan sel tampilan tabel Anda dari BaseTableViewCell.

Selanjutnya, Inside UIViewController, yang mengimplementasikan UITableViewDelegate, membuat dua pengenal isyarat untuk menangani gesekan ke kiri dan kanan.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    [self.tableView registerNib:[UINib nibWithNibName:CUSTOM_CELL_NIB_NAME bundle:nil] forCellReuseIdentifier:CUSTOM_CELL_ID];

    UISwipeGestureRecognizer* leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleLeftSwipe:)];
    leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
    [self.tableView addGestureRecognizer:leftSwipeRecognizer];

    UISwipeGestureRecognizer* rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleRightSwipe:)];
    rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
    [self.tableView addGestureRecognizer:rightSwipeRecognizer];
}

Daripada menambahkan dua penangan swipe

- (void)handleLeftSwipe:(UISwipeGestureRecognizer*)recognizer
{
    CGPoint point = [recognizer locationInView:self.tableView];
    NSIndexPath* index = [self.tableView indexPathForRowAtPoint:point];

    UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:index];

    if([cell respondsToSelector:@selector(showCustomView)])
    {
        [cell performSelector:@selector(showCustomView)];
    }
}

- (void)handleRightSwipe:(UISwipeGestureRecognizer*)recognizer
{
    CGPoint point = [recognizer locationInView:self.tableView];
    NSIndexPath* index = [self.tableView indexPathForRowAtPoint:point];

    UITableViewCell* cell = [self.tableView cellForRowAtIndexPath:index];

    if([cell respondsToSelector:@selector(hideCustomView)])
    {
        [cell performSelector:@selector(hideCustomView)];
    }
}

Sekarang, di dalam cellForRowAtIndexPath, dari UITableViewDelegate, Anda dapat membuat UIView khusus dan melampirkannya ke sel dequeued.

-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CustomCellTableViewCell* cell = (CustomCellTableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"CustomCellTableViewCell" forIndexPath:indexPath];

    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"CellCustomView"
                                                      owner:nil
                                                    options:nil];

    CellCustomView* customView = (CellCustomView*)[ nibViews objectAtIndex: 0];

    cell.customView = customView;

    return cell;
}

Tentu saja, cara memuat UIView khusus ini hanya untuk contoh ini. Kelola sesuai keinginan Anda.

slobodans
sumber