Scorri fino a Elimina e il pulsante "Altro" (come nell'app Mail su iOS 7)


Come creare un pulsante "altro" quando l'utente fa scorrere una cella nella vista tabella (come l'app di posta in iOS 7)

Ho cercato queste informazioni sia qui che nel forum Cocoa Touch, ma non riesco a trovare la risposta e spero che qualcuno più intelligente di me possa darmi una soluzione.

Mi piacerebbe che quando l'utente scorre una cella di visualizzazione tabella, per visualizzare più di un pulsante di modifica (di default è il pulsante Elimina). Nell'app Mail per iOS 7 puoi scorrere per eliminare, ma compare un pulsante "ALTRO".



Answers



Ho creato una nuova libreria per implementare i pulsanti swippable che supporta una varietà di transizioni e pulsanti espandibili come l'app di posta iOS 8.

https://github.com/MortimerGoro/MGSwipeTableCell

Questa libreria è compatibile con tutti i diversi modi per creare un UITableViewCell e il suo test su iOS 5, iOS 6, iOS 7 e iOS 8.

Ecco un esempio di alcune transizioni:

Transizione di confine:

Transizione clip

Transizione 3D:




Come implementare

Sembra che iOS 8 apra questa API. Suggerimenti di tale funzionalità sono presenti in Beta 2.

Per far funzionare qualcosa, implementare i seguenti due metodi sul delegato di UITableView per ottenere l'effetto desiderato (vedere il significato per un esempio).

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


Problemi noti

La documentazione dice tableView: commitEditingStyle: forRowAtIndexPath è:

"Non è stata richiesta la modifica delle azioni utilizzando UITableViewRowAction - al contrario verrà richiamato il gestore dell'azione."

Tuttavia, lo swiping non funziona senza di esso. Anche se il metodo stub è vuoto, per ora ne ha ancora bisogno. Questo è ovviamente un bug nella beta 2.


fonti

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

Risposta originale: https://.com/a/24540538/870028


Aggiornare:

Esempio di codice con questo funzionamento (In Swift): http://dropbox.com/s/0fvxosft2mq2v5m/DeleteRowExampleSwift.zip

Il codice di esempio contiene questo metodo facile da seguire in MasterViewController.swift, e con questo metodo si ottiene il comportamento mostrato nella schermata 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];
}



Come implementare

iOS 8 ha aperto questa API.

Per ottenere questo funzionamento funzionante, implementare i seguenti due metodi sul delegato di UITableView per ottenere l'effetto desiderato (vedere il codice per un esempio).

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


Problemi noti

Il file UITableView.h dice quanto segue riguardo a tableView: commitEditingStyle: forRowAtIndexPath :

"Non è stata richiesta la modifica delle azioni utilizzando UITableViewRowAction - al contrario verrà richiamato il gestore dell'azione."

Tuttavia, lo scorrimento non è abilitato senza di esso. Questo sembra essere un bug.


fonti

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


Codice d'esempio:

Questo si basa sul modello predefinito di Master-Detail Application (è in Swift): http://dropbox.com/s/0fvxosft2mq2v5m/DeleteRowExampleSwift.zip




La risposta di Johnny è quella giusta da alzare. Sto solo aggiungendo questo sotto nell'obiettivo-c per renderlo più chiaro ai principianti (e quelli di noi che si rifiutano di imparare la sintassi Swift :)

Assicurati di dichiarare uitableviewdelegate e disponi dei seguenti metodi:

 -(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...
    }



Questa è (piuttosto ridicolmente) un'API privata.

I seguenti due metodi sono privati ​​e inviati al delegato di UITableView:

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

Sono piuttosto auto-esplicativi.




Spero che non puoi aspettare fino a quando Apple ti darà quello che ti serve, giusto? Quindi ecco la mia opzione.

Crea una cella personalizzata. Ci sono due visualizzazioni

1. upper
2. lower

Nella vista inferiore, aggiungi tutti i pulsanti di cui hai bisogno. Affronta le sue azioni come qualsiasi altra IBAction. puoi decidere il tempo di animazione, lo stile e qualsiasi altra cosa.

ora aggiungi un uiswipegesture alla vista superiore e rivela la tua vista inferiore sul gesto di scorrimento. L'ho già fatto e è l'opzione più semplice per quanto mi riguarda.

Spero che aiuto.




Per migliorare la risposta di Johnny, ora è possibile utilizzare l'API pubblica come segue:

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



Questo non è possibile usando l'SDK standard. Tuttavia ci sono varie soluzioni di terze parti che più o meno imitano il comportamento in Mail.app. Alcuni di essi (ad es. MCSwipeTableViewCell , DAContextMenuTableViewController , RMSwipeTableViewCell ) rilevano gli sweep utilizzando i riconoscitori di gesti, alcuni di essi (ad esempio SWTableViewCell ) inseriscono un secondo UISScrollView sotto lo standard UITableViewCellScrollView (sottoview privato di UITableViewCell ) e alcuni di essi modificano il comportamento di UITableViewCellScrollView .

Mi piace l'ultimo approccio dato che la gestione del tocco sembra più naturale. In particolare, MSCMoreOptionTableViewCell è buono. La tua scelta può variare a seconda delle tue esigenze specifiche (se hai bisogno anche di un riquadro da sinistra a destra, se hai bisogno della compatibilità con iOS 6, ecc.). Si noti inoltre che la maggior parte di questi approcci presenta un onere: possono facilmente rompere in una versione futura di iOS se Apple apporta modifiche nella gerarchia di sottotitoli di UITableViewCell .




È necessario willTransitionToState:(UITableViewCellStateMask)state in sottoclasse lo stato di UITableViewCell e sottoclasse willTransitionToState:(UITableViewCellStateMask)state che viene chiamato ogni volta che l'utente fa scorrere la cella. I flag di state ti faranno sapere se il pulsante Elimina è visualizzato e mostra / nascondi il tuo pulsante Altro lì.

Sfortunatamente questo metodo non offre né la larghezza del pulsante Elimina né il tempo di animazione. Quindi è necessario che tu osservi e inserisci nel tuo codice il frame e il tempo di animazione del tuo pulsante More (personalmente penso che Apple debba fare qualcosa al riguardo).




Codice di versione Swift 3 senza utilizzare alcuna libreria:

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

}



Per una programmazione veloce

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
}



QUESTO POTREBBE AIUTARVI.

-(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...
    }



Stavo cercando di aggiungere la stessa funzionalità alla mia app, e dopo aver esaminato tanti tutorial diversi ( raywenderlich è la migliore soluzione fai-da-te), ho scoperto che Apple ha la sua classe UITableViewRowAction , che è molto utile.

Devi cambiare il metodo boilerpoint di Tableview a questo:

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

Puoi saperne di più su questo sito . La documentazione di Apple è davvero utile per cambiare il colore di sfondo:

Il colore di sfondo del pulsante di azione.

Dichiarazione OBJECTIVE-C @property (nonatomic, copy) UIColor * backgroundColor Discussion Utilizzare questa proprietà per specificare il colore di sfondo per il proprio pulsante. Se non si specifica un valore per questa proprietà, UIKit assegna un colore predefinito in base al valore nella proprietà style.

Disponibilità Disponibile in iOS 8.0 e versioni successive.

Se vuoi cambiare il carattere del pulsante, è un po 'più complicato. Ho visto un altro post su SO. Per fornire il codice e il link, ecco il codice che hanno usato lì. Dovresti cambiare l'aspetto del pulsante. Dovresti fare un riferimento specifico a tableviewcell, altrimenti cambieresti l'aspetto del pulsante in tutta l'app (non volevo, ma potresti, non lo so :))

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

Swift:

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

Questa è la versione più semplice e più affollata IMHO. Spero che sia d'aiuto.

Aggiornamento: ecco la versione 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]
}



Risposta effettiva a Swift 3

Questa è l'UNICA funzione di cui hai bisogno. Non hai bisogno delle funzioni CanEdit o CommitEditingStyle per le azioni personalizzate.

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



Ecco un modo piuttosto fragile di farlo che non coinvolge API private o la costruzione del proprio sistema. Stai proteggendo le tue scommesse dal fatto che Apple non si rompe e speriamo che rilascino un'API con cui puoi sostituire queste poche righe di codice.

  1. KVO self.contentView.superview.layer.sublayer. Fai questo in init. Questo è il livello di UIScrollView. Non puoi KVO "sottoview".
  2. Quando le modifiche al sottoview cambiano, trova la vista di conferma dell'eliminazione all'interno di scrollview.subviews. Questo è fatto nel richiamare osservare.
  3. Raddoppia la dimensione di quella vista e aggiungi un UIButton a sinistra della sua unica sottoview. Ciò è anche fatto nel richiamare osservare. L'unica sottoview della visualizzazione di conferma dell'eliminazione è il pulsante Elimina.
  4. (facoltativo) L'evento UIButton deve cercare self.superview fino a quando non trova un UITableView e quindi chiama un metodo datasource o delegate, come ad esempio tableView: commitCustomEditingStyle: forRowAtIndexPath :. È possibile trovare indexPath della cella utilizzando [tableView indexPathForCell: self].

Ciò richiede anche l'implementazione delle callback di delegazione di modifica della visualizzazione di tabelle standard.

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



C'è una libreria stupefacente chiamato SwipeCellKit, dovrebbe guadagnare più riconoscimento. A mio parere è più fredda MGSwipeTableCell. Quest'ultimo non replica completamente il comportamento delle cellule della applicazione Posta, mentre SwipeCellKitlo fa. Dare un'occhiata




A partire da iOS 11 questo è a disposizione del pubblico in UITableViewDelegate. Ecco alcuni esempi di codice:

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

Anche disponibile:

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

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




Qui è una soluzione semplice. E 'in grado di visualizzare e nascondere UIView personalizzato all'interno UITableViewCell. Visualizzazione logica è contenuto all'interno di una classe estesa da 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

Per ottenere questa funzionalità, semplice Ampliate la vostra vista tabella delle cellule da BaseTableViewCell.

Avanti, Dentro UIViewController, che attuano UITableViewDelegate, creare due sistemi di riconoscimento gesto di gestire fendenti a destra ea sinistra.

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

Di aggiungere due gestori magnetiche

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

Ora, all'interno cellForRowAtIndexPath, di UITableViewDelegate, è possibile creare UIView personalizzato e allegarlo alla cella rimosse dalla coda.

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

Naturalmente, questo modo di carico di UIView personalizzati è proprio per questo esempio. Gestirlo come si desidera.




Swift 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
}



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])
}