Balayez pour supprimer et le bouton "Plus" (comme dans l'application Mail sur iOS 7)


Comment créer un "plus" bouton lorsque l'utilisateur glisser une cellule dans la vue tableau (comme l'application de messagerie dans ios 7)

J'ai cherché cette information ici et sur le forum de Cocoa Touch, mais je n'arrive pas à trouver la réponse et j'espère que quelqu'un de plus intelligent que moi pourra me donner une solution.

Je voudrais que lorsque l'utilisateur balaye une cellule de vue de table, pour afficher plus d'un bouton d'édition (il est par défaut le bouton de suppression). Dans l'application Mail pour iOS 7, vous pouvez balayer pour supprimer, mais il y a un bouton "PLUS" qui apparaît.


Answers



J'ai créé une nouvelle bibliothèque pour implémenter des boutons swippables qui supporte une variété de transitions et de boutons extensibles comme l'application de messagerie iOS 8.

https://github.com/MortimerGoro/MGSwipeTableCell

Cette bibliothèque est compatible avec toutes les différentes façons de créer un UITableViewCell et son testé sur iOS 5, iOS 6, iOS 7 et iOS 8.

Voici un échantillon de quelques transitions:

Transition frontalière

Transition de clip

Transition 3D:




Comment mettre en œuvre

Il semble que iOS 8 ouvre cette API. Des indices d'une telle fonctionnalité sont présents dans la version bêta 2.

Pour que quelque chose fonctionne, implémentez les deux méthodes suivantes sur le délégué de UITableView pour obtenir l'effet désiré (voir gist pour un exemple).

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


Problèmes connus

La documentation dit tableView: commitEditingStyle: forRowAtIndexPath est:

"Non appelé pour les actions d'édition utilisant UITableViewRowAction - le gestionnaire de l'action sera appelé à la place."

Cependant, le balayage ne fonctionne pas sans cela. Même si le talon de la méthode est vide, il en a toujours besoin, pour le moment. C'est de toute évidence un bug en beta 2.


Sources

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

Réponse originale: https://.com/a/24540538/870028


Mettre à jour:

Exemple de code avec ce fonctionnement (In Swift): http://dropbox.com/s/0fvxosft2mq2v5m/DeleteRowExampleSwift.zip

L'exemple de code contient cette méthode facile à suivre dans MasterViewController.swift, et avec cette méthode, vous obtenez le comportement indiqué dans la capture d'écran 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];
}



Comment mettre en œuvre

iOS 8 a ouvert cette API.

Pour que cela fonctionne, implémentez les deux méthodes suivantes sur le délégué de UITableView pour obtenir l'effet désiré (voir le code pour un exemple).

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


Problèmes connus

Le fichier UITableView.h dit ce qui suit concernant tableView: commitEditingStyle: forRowAtIndexPath :

"Non appelé pour les actions d'édition utilisant UITableViewRowAction - le gestionnaire de l'action sera appelé à la place."

Cependant, le balayage n'est pas activé sans cela. Cela semble être un bug.


Sources

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


Exemple de code:

Ceci est basé sur le modèle d'application Master-Detail par défaut (il est dans Swift): http://dropbox.com/s/0fvxosft2mq2v5m/DeleteRowExampleSwift.zip




La réponse de Johnny est la bonne à upvote. J'ajoute juste ceci ci-dessous dans l'objectif-c pour le rendre plus clair aux débutants (et ceux d'entre nous qui refusent d'apprendre la syntaxe de Swift :)

Assurez-vous de déclarer le uitableviewdelegate et disposez des méthodes suivantes:

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



C'est (plutôt ridiculement) une API privée.

Les deux méthodes suivantes sont privées et envoyées au délégué de UITableView:

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

Ils sont assez explicites.




J'espère que vous ne pouvez pas attendre jusqu'à ce que Apple vous donne ce que vous avez besoin non? Alors voici mon option.

Créer une cellule personnalisée Avoir deux uiviews dedans

1. upper
2. lower

En dessous, ajoutez les boutons dont vous avez besoin. Traitez ses actions comme n'importe quel autre IBActions. vous pouvez décider du temps d'animation, du style et de tout.

Maintenant, ajoutez une uiswipegesture à la vue supérieure et révélez votre vue inférieure sur le geste de balayage. Je l'ai fait avant et c'est l'option la plus simple en ce qui me concerne.

J'espère que cette aide.




Pour améliorer la réponse de Johnny, cela peut maintenant être fait en utilisant l'API publique comme suit:

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



Cela n'est pas possible en utilisant le SDK standard. Cependant, il existe différentes solutions tierces qui imitent plus ou moins le comportement dans Mail.app. Certains d'entre eux (par exemple MCSwipeTableViewCell , DAContextMenuTableViewController , RMSwipeTableViewCell ) détectent les balayages à l'aide de reconnaisseurs de mouvements, certains d'entre eux (par exemple SWTableViewCell ) placent un second UISScrollView sous la norme UITableViewCellScrollView (sous-vue privée de UITableViewCell ) et certains modifient le comportement de UITableViewCellScrollView .

Je préfère la dernière approche car la manipulation tactile est la plus naturelle. Plus précisément, MSCMoreOptionTableViewCell est bon. Votre choix peut varier en fonction de vos besoins spécifiques (que vous ayez besoin d'un panoramique de gauche à droite, de compatibilité avec iOS 6, etc.). Sachez également que la plupart de ces approches comportent un fardeau: elles peuvent facilement se casser dans une future version iOS si Apple apporte des modifications à la hiérarchie de sous- UITableViewCell UITableViewCell.




Vous devez sous- UITableViewCell et la sous-classe méthode willTransitionToState:(UITableViewCellStateMask)state qui est appelé chaque fois que l'utilisateur balaye la cellule. Les drapeaux d' state vous indiqueront si le bouton Supprimer est affiché et afficheront / masqueront votre bouton Plus à cet endroit.

Malheureusement, cette méthode ne vous donne ni la largeur du bouton Supprimer ni le temps d'animation. Vous avez donc besoin d'observer et de coder en dur le cadre et le temps d'animation de votre bouton Plus dans votre code (je pense personnellement qu'Apple doit faire quelque chose à ce sujet).




Swift 3 code de la version sans utiliser de bibliothèque:

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

}



Pour une programmation rapide

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
}



CECI POURRAIT VOUS AIDER.

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



Je cherchais à ajouter la même fonctionnalité à mon application, et après avoir passé par tant de tutoriels différents ( raywenderlich étant la meilleure solution de bricolage), j'ai découvert que Apple a sa propre classe UITableViewRowAction , ce qui est très pratique.

Vous devez changer la méthode de point chaud de Tableview à ceci:

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

Vous pouvez en savoir plus à ce sujet sur ce site . La propre documentation d' Apple est vraiment utile pour changer la couleur de fond:

La couleur d'arrière-plan du bouton d'action.

Déclaration OBJECTIVE-C @property (nonatomic, copy) UIColor * backgroundColor Discussion Utilisez cette propriété pour spécifier la couleur d'arrière-plan de votre bouton. Si vous ne spécifiez pas de valeur pour cette propriété, UIKit affecte une couleur par défaut basée sur la valeur de la propriété style.

Disponibilité Disponible sur iOS 8.0 et versions ultérieures.

Si vous voulez changer la police du bouton, c'est un peu plus compliqué. J'ai vu un autre post sur SO. Pour fournir le code ainsi que le lien, voici le code qu'ils ont utilisé. Vous devez changer l'apparence du bouton. Vous devriez faire une référence spécifique à tableviewcell, sinon vous changeriez l'apparence du bouton à travers votre application (je ne voulais pas ça, mais vous pourriez, je ne sais pas :))

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

Rapide:

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

C'est la version la plus facile et la plus calibrée à mon humble avis. J'espère que cela aide.

Mise à jour: Voici la version 3.0 de Swift:

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



Actual Swift 3 Réponse

C'est la seule fonction dont vous avez besoin. Vous n'avez pas besoin des fonctions CanEdit ou CommitEditingStyle pour les actions personnalisées.

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



Voici une façon un peu fragile de le faire qui n'implique pas les API privées ou la construction de votre propre système. Vous couvrez vos paris qu'Apple ne casse pas cela et que, espérons-le, ils vont sortir une API que vous pouvez remplacer avec ces quelques lignes de code.

  1. KVO self.contentView.superview.layer.sublayer. Faites ceci dans init. C'est la couche de UIScrollView. Vous ne pouvez pas "sous-vues" KVO.
  2. Lorsque les sous-vues sont modifiées, recherchez la vue de confirmation de suppression dans scrollview.subviews. Ceci est fait dans le rappel d'observation.
  3. Doublez la taille de cette vue et ajoutez un UIButton à gauche de sa seule sous-vue. Ceci est également fait dans le rappel d'observation. La seule sous-vue de la vue de confirmation de suppression est le bouton de suppression.
  4. (facultatif) L'événement UIButton doit rechercher self.superview jusqu'à ce qu'il trouve un UITableView, puis appelle une source de données ou une méthode de délégation que vous créez, telle que tableView: commitCustomEditingStyle: forRowAtIndexPath :. Vous pouvez trouver l'indexPath de la cellule en utilisant [tableView indexPathForCell: self].

Cela nécessite également que vous implémentiez la vue de table standard pour l'édition des rappels de délégué.

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



Il y a une bibliothèque étonnante appelée SwipeCellKit, elle devrait gagner plus de reconnaissance. À mon avis , il est plus froid que MGSwipeTableCell. Ce dernier ne se reproduit pas complètement le comportement des cellules de l'application Mail alors SwipeCellKitfait. Regarde




Comme d'iOS 11 c'est publiquement disponible en UITableViewDelegate. Voici quelques exemples de code:

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

Aussi disponible:

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

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




Voici une solution simple. Il est capable d'afficher et masquer UIView personnalisé à l'intérieur UITableViewCell. la logique d'affichage est contenu à l'intérieur de la classe étendue 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

Pour obtenir cette fonctionnalité, simple étendre votre point de vue de la table cellulaire BaseTableViewCell.

Ensuite, l'intérieur UIViewController, qui mettent en œuvre UITableViewDelegate, créer deux dispositifs de reconnaissance geste pour gérer swipes gauche et à droite.

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

Que d'ajouter deux gestionnaires 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)];
    }
}

Maintenant, à l'intérieur cellForRowAtIndexPath, de UITableViewDelegate, vous pouvez créer UIView personnalisé et l'attacher à la cellule 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;
}

Bien sûr, cette façon de chargement de UIView personnalisé est juste pour cet exemple. Gérer comme vous voulez.




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