Wischen Sie zu Löschen und die Schaltfläche "Mehr" (wie in der Mail-App unter iOS 7)


So erstellen Sie eine "mehr" -Schaltfläche, wenn Benutzer eine Zelle in der Tabellenansicht durchstreichen (wie die Mail-App in iOS 7)

Ich habe sowohl hier als auch im Cocoa Touch Forum nach diesen Informationen gesucht, aber ich kann die Antwort nicht finden und hoffe, dass mir jemand, der schlauer ist als ich, eine Lösung geben kann.

Ich möchte das, wenn der Benutzer eine Tabellensichtzelle durchsucht, um mehr als eine Bearbeitungsschaltfläche anzuzeigen (die Standardeinstellung ist die Löschschaltfläche). In der Mail-App für iOS 7 können Sie zum Löschen streichen, aber es gibt eine Schaltfläche "MEHR", die angezeigt wird.



Answers



Wie implementieren

Es sieht aus wie iOS 8 öffnet diese API. Hinweise zu dieser Funktionalität sind in Beta 2 enthalten.

Um etwas Funktionierendes zu erreichen, implementieren Sie die folgenden zwei Methoden im Delegierten Ihrer UITableView, um den gewünschten Effekt zu erzielen (siehe Gist für ein Beispiel).

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


Bekannte Probleme

Die Dokumentation besagt tableView: commitEditingStyle: forRowAtIndexPath ist:

"Keine Bearbeitungsaktionen mit UITableViewRowAction aufgerufen - der Handler der Aktion wird stattdessen aufgerufen."

Das Wischen funktioniert jedoch nicht ohne. Selbst wenn der Methodenstub leer ist, braucht er ihn fürs Erste. Das ist offensichtlich ein Bug in Beta 2.


Quellen

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

Originalantwort: https://.com/a/24540538/870028


Aktualisieren:

Beispielcode mit dieser Funktion (In Swift): http://dropbox.com/s/0fvxosft2mq2v5m/DeleteRowExampleSwift.zip

Der Beispielcode enthält diese leicht verständliche Methode in MasterViewController.swift, und mit dieser Methode erhalten Sie das Verhalten, das im OP-Screenshot gezeigt wird:

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



Ich habe eine neue Bibliothek erstellt, um swippable Tasten zu implementieren, die eine Vielzahl von Übergängen und erweiterbaren Tasten wie iOS 8 Mail App unterstützt.

https://github.com/MortimerGoro/MGSwipeTableCell

Diese Bibliothek ist kompatibel mit all den verschiedenen Möglichkeiten, eine UITableViewCell zu erstellen, und sie wurde unter iOS 5, iOS 6, iOS 7 und iOS 8 getestet.

Hier ein Beispiel einiger Übergänge:

Grenzübergang:

Clipübergang

3D-Übergang:




Wie implementieren

iOS 8 hat diese API geöffnet.

Implementieren Sie die folgenden zwei Methoden in Ihrem UITableView-Delegaten, um die gewünschte Wirkung zu erzielen (siehe Code für ein Beispiel).

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


Bekannte Probleme

Die Datei UITableView.h besagt zu tableView Folgendes : commitEditingStyle: forRowAtIndexPath :

"Keine Bearbeitungsaktionen mit UITableViewRowAction aufgerufen - der Handler der Aktion wird stattdessen aufgerufen."

Das Swiping ist jedoch ohne es nicht aktiviert. Das scheint ein Bug zu sein.


Quellen

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


Beispielcode:

Dies basiert auf der standardmäßigen Master-Detailanwendungsvorlage (in Swift): http://dropbox.com/s/0fvxosft2mq2v5m/DeleteRowExampleSwift.zip




Johnnys Antwort ist die richtige, um etwas zu sagen. Ich füge das unten in Ziel-c hinzu, um es Anfängern (und denen von uns, die sich weigern, Swift-Syntax zu lernen) klarer zu machen :)

Stellen Sie sicher, dass Sie das uitableviewdelegate deklarieren und über die folgenden Methoden verfügen:

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



Das ist (ziemlich lächerlich) eine private API.

Die folgenden zwei Methoden sind privat und werden an den Delegaten des UITableView gesendet:

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

Sie sind ziemlich selbsterklärend.




Ich hoffe, Sie können nicht warten, bis Apple Ihnen gibt, was immer Sie brauchen, richtig? Also hier ist meine Option.

Erstellen Sie eine benutzerdefinierte Zelle. Hab zwei Uiviews drin

1. upper
2. lower

Fügen Sie in der unteren Ansicht die Schaltflächen hinzu, die Sie benötigen. Vergeben Sie ihre Aktionen wie alle anderen IBActions. Sie können die Animationszeit, Stil und alles entscheiden.

Fügen Sie nun der oberen Ansicht eine uiswipegesture hinzu und zeigen Sie Ihre untere Ansicht der Wischgeste an. Ich habe das schon getan und es ist die einfachste Option für mich.

Hoffe diese Hilfe.




Um Johnnys Antwort zu verbessern, kann dies nun wie folgt über die öffentliche API erfolgen:

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



Das ist mit dem Standard-SDK nicht möglich. Es gibt jedoch verschiedene Lösungen von Drittanbietern, die das Verhalten in Mail.app mehr oder weniger imitieren. Einige von ihnen (zB MCSwipeTableViewCell , DAContextMenuTableViewController , RMSwipeTableViewCell ) erkennen Swipes mit Gestenerkennung, einige davon (zB SWTableViewCell ) setzen ein zweites UISScrollView unter das Standard UITableViewCellScrollView (private Untersicht von UITableViewCell ) und einige verändern das Verhalten von UITableViewCellScrollView .

Ich mag den letzten Ansatz am meisten, da sich das Touch-Handling am natürlichsten anfühlt. Insbesondere ist MSCMoreOptionTableViewCell gut. Ihre Auswahl kann je nach Ihren spezifischen Anforderungen variieren (ob Sie auch eine von links nach rechts gerichtete Schwenkfunktion benötigen, ob Sie iOS 6-Kompatibilität benötigen usw.). UITableViewCell Sie auch, dass die meisten dieser Ansätze mit einer Last verbunden sind: Sie können leicht in eine zukünftige iOS-Version einbrechen, wenn Apple Änderungen in der UITableViewCell Unteransicht-Hierarchie vornimmt.




Sie müssen die UITableViewCell und Subclass-Methode " willTransitionToState:(UITableViewCellStateMask)state die aufgerufen wird, wenn der Benutzer die Zelle durchsucht. Die Statusflaggen informieren Sie darüber, ob die Schaltfläche "Löschen" angezeigt wird, und zeigen dort die Schaltfläche "Mehr" an bzw. blenden sie aus.

Leider gibt diese Methode weder die Breite der Schaltfläche Löschen noch die Animationszeit an. Sie müssen also den Frame und die Animationszeit Ihrer More-Taste in Ihrem Code beobachten und fest codieren (ich persönlich glaube, dass Apple etwas dagegen tun muss).




Swift 3 Versionscode ohne Verwendung einer Bibliothek:

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

}



Für schnelle Programmierung

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
}



DIES KÖNNTE IHNEN HELFEN.

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



Ich wollte meiner App die gleiche Funktionalität hinzufügen, und nach so vielen verschiedenen Tutorials ( raywenderlich ist die beste DIY-Lösung) habe ich herausgefunden, dass Apple seine eigene UITableViewRowAction Klasse hat, was sehr praktisch ist.

Sie müssen die Boilerpoint-Methode von Tableview folgendermaßen ändern:

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

Mehr dazu erfahren Sie auf dieser Site . Apples eigene Dokumentation ist sehr nützlich, um die Hintergrundfarbe zu ändern:

Die Hintergrundfarbe der Aktionstaste

Deklaration OBJECTIVE-C @property (nonatomic, copy) UIColor * backgroundColor Diskussion Mit dieser Eigenschaft legen Sie die Hintergrundfarbe für Ihre Schaltfläche fest. Wenn Sie keinen Wert für diese Eigenschaft angeben, weist UIKit eine Standardfarbe basierend auf dem Wert in der style-Eigenschaft zu.

Verfügbarkeit Verfügbar in iOS 8.0 und höher.

Wenn Sie die Schriftart der Schaltfläche ändern möchten, ist das etwas komplizierter. Ich habe einen anderen Beitrag über SO gesehen. Um den Code und den Link bereitzustellen, ist hier der Code, den sie dort verwendet haben. Sie müssten das Erscheinungsbild der Schaltfläche ändern. Du müsstest einen speziellen Verweis auf tableviewcell machen, sonst würdest du das Erscheinungsbild der Schaltfläche in deiner App ändern (ich wollte das nicht, aber vielleicht weißt du es nicht :))

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

Schnell:

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

Dies ist die einfachste und stromlinienförmigste Version IMHO. Ich hoffe es hilft.

Update: Hier ist die Swift 3.0 Version:

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



Aktuelle Swift 3 Antwort

Dies ist die EINZIGE Funktion, die Sie benötigen. Sie benötigen CanEdit- oder CommitEditingStyle-Funktionen für benutzerdefinierte Aktionen nicht.

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



Hier ist eine etwas zerbrechliche Vorgehensweise, die keine privaten APIs beinhaltet und kein eigenes System erstellt. Sie sichern Ihre Wetten ab, dass Apple dies nicht bricht und hoffentlich eine API veröffentlichen wird, mit der Sie diese wenigen Codezeilen ersetzen können.

  1. KVO self.contentView.superview.layer.sublayer. Mach das in init. Dies ist die Ebene des UIScrollView. Sie können keine KVO-Unteransichten erstellen.
  2. Wenn sich Unteransichten ändern, suchen Sie die Löschbestätigungsansicht in scrollview.subviews. Dies geschieht im Observback-Callback.
  3. Verdoppeln Sie die Größe dieser Ansicht und fügen Sie eine UIButton links neben ihrer einzigen Unteransicht hinzu. Dies geschieht auch im Callback beobachten. Die einzige Unteransicht der Löschbestätigungsansicht ist die Löschschaltfläche.
  4. (optional) Das UIButton-Ereignis sollte self.superview nachschlagen, bis es eine UITableView findet und dann eine von Ihnen erstellte Datenquellen- oder Delegatmethode aufrufen, z. B. tableView: commitCustomEditingStyle: forRowAtIndexPath :. Sie können den indexPath der Zelle finden, indem Sie [tableView indexPathForCell: self] verwenden.

Dies erfordert auch, dass Sie die Callbacks der Standardtabellenansicht zur Bearbeitung von Delegaten implementieren.

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



Es gibt eine erstaunliche Bibliothek namens SwipeCellKit , die mehr Anerkennung erhalten sollte. Meiner Meinung nach ist es cooler als MGSwipeTableCell . Letzteres repliziert das Verhalten der Zellen der Mail-App nicht vollständig, SwipeCellKit hingegen. Guck mal




Hier ist eine einfache Lösung. Es ist in der Lage, benutzerdefinierte UIView innerhalb von UITableViewCell anzuzeigen und auszublenden. Die Darstellungslogik ist in der erweiterten Klasse von UITableViewCell, BaseTableViewCell enthalten.

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

Um diese Funktionalität zu erhalten, erweitern Sie einfach Ihre Tabellensichtzelle von BaseTableViewCell.

Als Nächstes erstellen Inside UIViewController, die UITableViewDelegate implementieren, zwei Gestenerkenner, um linke und rechte Wischbewegungen zu verarbeiten.

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

Fügen Sie zwei Swipe-Handler hinzu

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

Nun können Sie in cellForRowAtIndexPath von UITableViewDelegate benutzerdefinierte UIView erstellen und an die aus der Warteschlange entfernte Zelle anhängen.

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

Natürlich ist diese Art des Ladens von benutzerdefiniertem UIView nur für dieses Beispiel. Verwalte es so, wie du es möchtest.




Ab iOS 11 ist dies in UITableViewDelegate öffentlich verfügbar. Hier ist ein Beispielcode:

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

Auch verfügbar:

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

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




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