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


Wie man eine "mehr" -Schaltfläche erstellt, wenn der Benutzer eine Zelle in der Tabellenansicht wischt (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 ich hoffe, dass jemand, der schlauer ist als ich, mir eine Lösung geben kann.

Ich möchte, dass, wenn der Benutzer eine Tabellenansichtszelle wischt, mehr als eine Bearbeitungsschaltfläche angezeigt wird (er ist standardmäßig die Löschschaltfläche). In der Mail-App für iOS 7 können Sie zum Löschen wischen, aber es gibt eine Schaltfläche "MEHR", die angezeigt wird.



Answers


Ich habe eine neue Bibliothek erstellt, um Swippable-Buttons zu implementieren, die eine Vielzahl von Übergängen und erweiterbare Buttons wie iOS 8 Mail App unterstützen.

https://github.com/MortimerGoro/MGSwipeTableCell

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

Hier ein Beispiel einiger Übergänge:

Grenzübergang:

Clip-Übergang

3D-Übergang:




Wie zu implementieren

Offenbar öffnet iOS 8 diese API. Hinweise auf diese Funktionalität gibt es in Beta 2.

Um etwas zum Laufen zu bringen, implementieren Sie die folgenden zwei Methoden in Ihrem UITableView-Delegaten, um den gewünschten Effekt zu erhalten (siehe Beispiel).

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


Bekannte Probleme

Die Dokumentation sagt tableView: commitEditingStyle: forRowAtIndexPath ist:

"Edit-Aktionen werden nicht mit UITableViewRowAction aufgerufen - der Handler der Aktion wird stattdessen aufgerufen."

Das Wischen funktioniert jedoch ohne es nicht. Auch wenn der Methoden-Stub leer ist, braucht er es vorerst noch. Dies ist offensichtlich ein Fehler in Beta 2.


Quellen

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

Ursprüngliche Antwort: https://.com/a/24540538/870028


Aktualisieren:

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

Der Beispielcode enthält diese einfach zu befolgende Methode in MasterViewController.swift und mit dieser Methode erhalten Sie das Verhalten, das im OP-Screenshot angezeigt 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];
}



Wie zu implementieren

iOS 8 öffnete diese API.

Damit das funktionieren kann, implementieren Sie die folgenden zwei Methoden auf dem Delegaten Ihres UITableView, um den gewünschten Effekt zu erhalten (siehe Code für ein Beispiel).

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


Bekannte Probleme

Die UITableView.h-Datei sagt Folgendes in Bezug auf TableView: commitEditingStyle: forRowAtIndexPath :

"Edit-Aktionen werden nicht mit UITableViewRowAction aufgerufen - der Handler der Aktion wird stattdessen aufgerufen."

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


Quellen

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


Beispielcode:

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




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

Stellen Sie sicher, dass Sie das Attribut "uitableviewdelegate" deklarieren und die folgenden Methoden verwenden:

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



Dies ist (eher 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 du kannst nicht warten bis Apple dir was gibt was du brauchst, richtig? Also hier ist meine Option.

Erstellen Sie eine benutzerdefinierte Zelle. Habe zwei uViews drin

1. upper
2. lower

In der unteren Ansicht fügen Sie die Schaltflächen hinzu, die Sie benötigen. Verteilen Sie ihre Aktionen genau wie andere IBActions. Sie können die Animationszeit, den Stil und alles andere bestimmen.

Fügen Sie jetzt einen Uiswipegesture zur oberen Ansicht hinzu und zeigen Sie Ihre untere Ansicht auf Swipe-Geste. Ich habe das schon mal gemacht und es ist die einfachste Option für mich.

Hoffe, dass Hilfe.




Um Johnnys Antwort zu verbessern, kann dies nun wie folgt mit der öffentlichen API durchgeführt werden:

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



Dies ist mit dem Standard-SDK nicht möglich. Es gibt jedoch verschiedene Lösungen von Drittanbietern, die mehr oder weniger das Verhalten in Mail.app nachahmen. Einige von ihnen (z. B. MCSwipeTableViewCell , DAContextMenuTableViewController , RMSwipeTableViewCell ) erkennen Swipes mit Gestenerkennung, einige von ihnen (z. B. SWTableViewCell ) setzen eine zweite UISScrollView unterhalb der Standard UITableViewCellScrollView (private Subview von UITableViewCell ) und einige von ihnen ändern das Verhalten von UITableViewCellScrollView .

Ich mag den letzten Ansatz, da sich die Touch-Bedienung am natürlichsten anfühlt. Insbesondere ist MSCMoreOptionTableViewCell gut. Ihre Auswahl kann je nach Ihren spezifischen Anforderungen variieren (ob Sie auch einen Schwenk von links nach rechts 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 UITableViewCell vornimmt.




Sie müssen die Klasse UITableViewCell und die Unterklassenmethode willTransitionToState:(UITableViewCellStateMask)state die aufgerufen wird, wenn der Benutzer die Zelle wischt. Die state Flags informieren Sie, wenn die Schaltfläche Löschen angezeigt wird, und zeigen / verbergen dort die Schaltfläche Mehr.

Leider gibt diese Methode weder die Breite der Schaltfläche Löschen noch die Animationszeit. Also müssen Sie den Frame und die Animationszeit Ihres More-Buttons in Ihren Code einbeziehen und fest codieren (ich persönlich denke, 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 nachdem ich so viele verschiedene Tutorials durchgelesen hatte ( raywender ist die beste DIY-Lösung), fand ich heraus, dass Apple seine eigene UITableViewRowAction Klasse hat, was sehr praktisch ist.

Sie müssen die Tabellenpunktmethode des TableViews 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]
  }

Sie können mehr darüber auf dieser Seite herausfinden. Apples eigene Dokumentation ist wirklich nützlich, um die Hintergrundfarbe zu ändern:

Die Hintergrundfarbe der Aktionsschaltfläche.

Deklaration OBJECTIVE-C @property (nichtatomare, Kopie) UIColor * backgroundColor Diskussion Verwenden Sie diese Eigenschaft, um die Hintergrundfarbe für Ihre Schaltfläche anzugeben. Wenn Sie für diese Eigenschaft keinen Wert angeben, ordnet UIKit eine Standardfarbe basierend auf dem Wert in der Stileigenschaft zu.

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

Wenn Sie die Schriftart der Schaltfläche ändern möchten, ist es ein wenig komplizierter. Ich habe einen anderen Post über SO gesehen. Um den Code und den Link zur Verfügung zu stellen, hier ist der Code, den sie dort verwendeten. Sie müssten das Erscheinungsbild der Schaltfläche ändern. Sie müssten einen spezifischen Bezug zu tableviewcell herstellen, sonst würden Sie das Erscheinungsbild der Schaltfläche in Ihrer App ändern (das wollte ich nicht, aber Sie könnten, ich weiß 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 am stärksten gestraffte 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]
}



Tatsächliche Swift 3 Antwort

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

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 fragile Art, dies zu tun, die keine privaten APIs beinhaltet oder ein eigenes System erstellt. Sie sichern Ihre Wetten ab, dass Apple dies nicht bricht und dass sie hoffentlich eine API veröffentlichen, mit der Sie diese wenigen Zeilen Code ersetzen können.

  1. KVO self.contentView.superview.layer Unterschicht. Tun Sie dies in Init. Dies ist die UIScrollView-Ebene. Sie können KVO-Unteransichten nicht erstellen.
  2. Wenn sich die Unteransichten ändern, suchen Sie die Löschbestätigungsansicht in scrollviewviews. Dies geschieht im Callback beobachten.
  3. Verdoppeln Sie die Größe dieser Ansicht und fügen Sie links neben der einzigen Unteransicht eine UIB-Schaltfläche hinzu. Dies geschieht auch im Callback beobachten. Die einzige Unteransicht der Löschbestätigungsansicht ist die Schaltfläche Löschen.
  4. (optional) Das UIButton-Ereignis sollte sich in self.superview nachschlagen, bis es ein UITableView findet und dann eine von Ihnen erstellte Datenquellen- oder Delegatmethode aufruft, z. B. tableView: commitCustomEditingStyle: forRowAtIndexPath :. Sie können den Indexpfad der Zelle mithilfe von [tableView indexPathForCell: self] finden.

Dies erfordert auch, dass Sie die Callbacks der Standardtabellenansicht zum Bearbeiten 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 ist eine erstaunliche Bibliothek genannt SwipeCellKit, sollte es mehr Anerkennung zu gewinnen. Meiner Meinung nach ist es kühler als MGSwipeTableCell. Letzteres nicht vollständig das Verhalten der Zellen des Mail - App replizieren , während der SwipeCellKitFall ist. Guck mal




Ab iOS 11 ist dies in öffentlich zugänglichen UITableViewDelegate. 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




Hier ist eine einfache Lösung. Es ist in der Lage benutzerdefinierte UIView innerhalb UITableViewCell ein- und ausgeblendet. Anzeige der Logik innerhalb der Klasse enthaltenen UITableViewCell erweitert, 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

Um diese Funktionalität zu erhalten, einfach Ihre Tabellenansicht Zelle von BaseTableViewCell erstrecken.

Als nächstes Innerhalb UIViewController, die UITableViewDelegate implementieren, erstellen Sie zwei Geste Erkenner linken und rechten Seitenhiebe zu behandeln.

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

Als fügen Sie zwei Swipe-Handler

- (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 innerhalb cellForRowAtIndexPath, von UITableViewDelegate, können Sie benutzerdefinierte UIView und hängen Sie es an die Zelle Aus der Warteschleife angenommene erstellen.

-(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 der Beladung von benutzerdefinierten UIView nur für dieses Beispiel. Verwalten Sie es, wie Sie wollen.




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