Wischen Sie zum Löschen und klicken Sie auf die Schaltfläche „Mehr“ (wie in der Mail-App unter iOS 7).

246

So erstellen Sie eine Schaltfläche "Mehr", 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 scheinbar nicht finden und hoffe, dass jemand, der klüger 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 (standardmäßig ist die Löschschaltfläche). In der Mail-App für iOS 7 können Sie zum Löschen wischen, aber es wird eine Schaltfläche "MEHR" angezeigt.

Geben Sie hier die Bildbeschreibung ein

Guy Kahlon
quelle
Um die Schaltfläche "Löschen" hinzuzufügen, implementiere ich die folgenden zwei Funktionen. - (BOOL) tableView: (UITableView *) tableView canEditRowAtIndexPath: (NSIndexPath *) indexPath; - (void) tableView: (UITableView *) tableView commitEditingStyle: (UITableViewCellEditingStyle) editorStyle forRowAtIndexPath: (NSIndexPath *) indexPath; Und ich möchte den Button "Mehr" daneben hinzufügen.
Guy Kahlon
3
@MonishBansal Bansal Es sieht so aus, als hätte jemand in diesem Thread ( devforums.apple.com/message/860459#860459 im Apple-Entwicklerforum) seine eigene Implementierung erstellt. Sie können ein Projekt finden, das macht, was Sie wollen auf GitHub: github.com/daria-kopaliani/DAContextMenuTableViewController
Guy Kahlon
8
@ GuyKahlonMatrix danke für die Lösung, die wie ein Zauber funktioniert. Diese Frage ist das Ergebnis Nr. 1 bei vielen Google-Suchanfragen, und die Leute sind gezwungen, ihr Wissen mithilfe der Kommentare auszutauschen, weil ein Typ entschieden hat, dass es hilfreicher ist, die Frage zu schließen und stattdessen Demokratie zu predigen. Dieser Ort braucht eindeutig bessere Mods.
Şafak Gezer
2
Wenn Sie auf iOS 8 abzielen können, ist meine Antwort unten genau das, was Sie wollen.
Johnny

Antworten:

126

Implementierung

Es sieht so aus, als würde iOS 8 diese API öffnen. Hinweise auf solche Funktionen sind in Beta 2 enthalten.

Um etwas zum Laufen zu bringen, implementieren Sie die folgenden zwei Methoden auf dem Delegierten von UITableView, um den gewünschten Effekt zu erzielen (ein Beispiel finden Sie in der Übersicht).

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


Bekannte Probleme

In der Dokumentation heißt es: tableView: commitEditingStyle: forRowAtIndexPath lautet:

"Nicht zum Bearbeiten von Aktionen mit UITableViewRowAction aufgerufen - der Handler der Aktion wird stattdessen aufgerufen."

Das Wischen funktioniert jedoch nicht ohne. Auch wenn der Methodenstub leer ist, wird er vorerst noch benötigt. 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://stackoverflow.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 verfolgende Methode in MasterViewController.swift. Mit nur dieser Methode erhalten Sie das im OP-Screenshot gezeigte Verhalten:

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

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

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

    return [deleteRowAction, moreRowAction];
}
Johnny
quelle
1
Dies scheint korrekt zu sein, aber in Xcode 6 GM scheint die Wischgeste nicht zu funktionieren. Auf die editActions kann weiterhin zugegriffen werden, indem die Tabellenansicht in den Bearbeitungsmodus versetzt wird. Findet sonst noch jemand, dass Swipe nicht funktioniert?
Siegfoult
@Siegfoult Haben Sie versucht, tableView: commitEditingStyle: forRowAtIndexPath: zu implementieren (auch wenn es leer gelassen wurde)?
Johnny
Ich habe nicht in Ziel c gearbeitet. Gleicher Code, den ich geschrieben habe. Bitte schlagen Sie einige Hinweise vor.
Solid Soft
@SolidSoft Haben Sie ein Beispielprojekt, das ich mir ansehen könnte? Auf diese Weise könnte ich vielleicht besser helfen.
Johnny
3
Um meinen eigenen Kommentar zu beantworten. Sie rufen an tableView.editing = false( NOin objc) und die Zelle wird "geschlossen".
Ben Lachman
121

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

https://github.com/MortimerGoro/MGSwipeTableCell

Diese Bibliothek ist mit all den verschiedenen Möglichkeiten zum Erstellen einer UITableViewCell kompatibel und wurde unter iOS 5, iOS 6, iOS 7 und iOS 8 getestet.

Hier ein Beispiel einiger Übergänge:

Grenzübergang:

Grenzübergang

Clip-Übergang

Clip-Übergang

3D-Übergang:

Geben Sie hier die Bildbeschreibung ein

MortimerGoro
quelle
1
Gute Arbeit! Es wäre fantastisch, Rückrufe zu haben, um Animationen anzupassen.
Pacu
1
@ MortimerGoro Netter Arbeiter. Es sieht gut aus. Ich versuche, einen ähnlichen Effekt in einem meiner Android-Projekte zu implementieren. Bitte sagen Sie mir, wie ich dies in Android erreichen kann.
Nitesh Kumar
Auf iOS 8 + iPad bekomme ich einfach nicht das Wischen.
ScorpionKing2k5
Dies ist eine erstaunliche Bibliothek und was sehr gut ist, ist, dass sie immer noch Unterstützung hat.
Confile
@ MortimerGoro, ich habe es mit dem Framework "MGSwipeTableCel" versucht, aber das Problem ist, wenn ich meine Tabelle neu lade, wird die Wischschaltfläche ausgeblendet. Irgendwelche Abhilfemaßnahmen für dieses Problem.
Ganesh Guturi
71

Johnnys Antwort ist die richtige, um zu stimmen. Ich füge dies unten in Ziel-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 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...
    }
timothykc
quelle
1
wichtig zu erwähnen canEditRowAtIndexPath
Heckscheibe
Wenn ich die Tabelle nach dem Wischen der Zelle neu lade, sind diese Wischschaltflächen sichtbar oder ausgeblendet?
Ganesh Guturi
25

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

Die folgenden zwei Methoden sind privat und werden an den Delegierten von UITableView gesendet:

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

Sie sind ziemlich selbsterklärend.

Jonathan.
quelle
4
Apple hat diese Funktion mit iOS 8 geöffnet. Siehe Johnnys Antwort unten.
Siegfoult
24

Um Johnnys Antwort zu verbessern, kann dies jetzt mithilfe der öffentlichen API wie folgt 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];
}
Arunabh Das
quelle
17

Ich hoffe, Sie können es kaum erwarten, bis Apple Ihnen alles gibt, was Sie brauchen, oder? Also hier ist meine Option.

Erstellen Sie eine benutzerdefinierte Zelle. Habe zwei Ansichten drin

1. upper
2. lower

Fügen Sie in der unteren Ansicht die gewünschten Schaltflächen hinzu. Behandeln Sie seine Aktionen wie alle anderen IBActions. Sie können die Animationszeit, den Stil und alles festlegen.

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

Hoffe das hilft.

Deepukjayan
quelle
7

Dies 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 (z. B. MCSwipeTableViewCell , DAContextMenuTableViewController , RMSwipeTableViewCell ) erkennen Wischbewegungen mithilfe von Gestenerkennern, andere (z. B. SWTableViewCell ) setzen eine zweite UISScrollView unter den Standard UITableViewCellScrollView(private Unteransicht von UITableViewCell) und einige ä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 einen Schwenk von links nach rechts benötigen, ob Sie iOS 6-Kompatibilität benötigen usw.). UITableViewCellBeachten Sie auch, dass die meisten dieser Ansätze mit einer Belastung verbunden sind: Sie können in einer zukünftigen iOS-Version leicht beschädigt werden, wenn Apple Änderungen in der Subview-Hierarchie vornimmt.

Ortwin Gentz
quelle
7

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

}
mriaz0011
quelle
6

Sie müssen eine Unterklasse UITableViewCellund eine Unterklassenmethode verwenden, willTransitionToState:(UITableViewCellStateMask)statedie aufgerufen werden, wenn der Benutzer die Zelle wischt. Die stateFlags zeigen an, ob die Schaltfläche Löschen angezeigt wird, und zeigen dort die Schaltfläche Mehr an / aus.

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

Khanh Nguyen
quelle
7
"Ich persönlich denke, Apple muss etwas dagegen tun". Genau. Haben Sie ihnen bereits einen Fehlerbericht / eine Funktionsanforderung geschrieben?
Tafkadasoh
4

Für eine 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
}
Michael Yagudaev
quelle
@bibscy Sie können gerne eine Bearbeitung vorschlagen. Ich habe Swift schon lange nicht mehr verwendet,
bin
3

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...
    }
SantoshIOS
quelle
3

Ich wollte meiner App die gleiche Funktionalität hinzufügen, und nachdem ich so viele verschiedene Tutorials durchgesehen hatte ( raywenderlich ist die beste DIY-Lösung), fand ich heraus, dass Apple eine eigene UITableViewRowActionKlasse 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]
  }

Weitere Informationen hierzu finden Sie auf dieser Website . Apples eigene Dokumentation ist sehr nützlich, um die Hintergrundfarbe zu ändern:

Die Hintergrundfarbe der Aktionsschaltfläche.

Deklaration ZIEL-C @property (nichtatomar, kopiert) 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, weist UIKit basierend auf dem Wert in der Stileigenschaft eine Standardfarbe zu.

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

Wenn Sie die Schriftart der Schaltfläche ändern möchten, ist dies etwas schwieriger. Ich habe einen anderen Beitrag auf SO gesehen. Um sowohl den Code als auch den Link bereitzustellen, ist hier der Code, den sie dort verwendet haben. Sie müssten das Erscheinungsbild der Schaltfläche ändern. Sie müssten einen spezifischen Verweis auf tableviewcell machen, sonst würden Sie das Erscheinungsbild der Schaltfläche in Ihrer App ändern (das wollte ich nicht, aber vielleicht, ich weiß 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 meiner Meinung nach die einfachste und optimierteste Version. 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]
}
Septronic
quelle
1
Vielen Dank für Ihre Antwort. Ich bin sicher, dass dies vielen Entwicklern helfen wird. Ja, Sie haben Recht, tatsächlich bietet Apple diese Lösung ab iOS 8 an. Leider bietet diese native Lösung nicht die volle Funktionalität. In der Mail-App von Apple haben Sie beispielsweise Schaltflächen von zwei Seiten (eine Schaltfläche von der linken Seite und drei von der rechten Seite). Mit der aktuellen API von Apple können Sie keine Schaltflächen für beide Seiten hinzufügen, und die aktuelle API unterstützt dies nicht Die Standardaktion, wenn der Benutzer lange zu jeder Seite wischt. Die beste Lösung für IMHO ist derzeit die Open Source MGSwipeTableCell.
Guy Kahlon
@GuyKahlon Ja, Sie haben absolut Recht in Bezug auf das Problem mit dem linken und rechten Wisch, und ich stimme zu, dass die MGSwipeTableCell für weitere Anpassungen die beste ist. Apples eigene Option ist nicht die raffinierteste Option, aber ich fand sie für einfache Aufgaben am einfachsten.
Septronic
@Septronic Könnten Sie bitte Ihren Code auf Swift 3 aktualisieren? shareMenu.nimmt keine addActionMethode. Danke
bibscy
@bibscy Ich habe die schnelle Version hinzugefügt. Benötigen Sie das Bit auch für das Attribut? sharemenu ist nur ein UIAlertController, also sollte es die Aktion ergreifen. Probieren Sie es aus und lassen Sie mich wissen, wenn Sie Glück haben :)
Septronic
3

Aktuelle 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]
}
William T.
quelle
3

Ab iOS 11 ist dies öffentlich verfügbar in 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;

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

Lewis
quelle
3

Swift 4 & iOs 11+

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

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

        handler(true)
        // handle deletion here
    }

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

        handler(true)
        // handle more here
    }

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

Ich habe tableViewCell verwendet , um mehrere Daten anzuzeigen. Nach dem Wischen () von rechts nach links in einer Zelle werden zwei Schaltflächen angezeigt. Genehmigen und ablehnen. Es gibt zwei Methoden: Die erste ist ApproveFunc, die ein Argument akzeptiert, und die andere ist RejectFunc, die ebenfalls nimmt ein Argument.

Geben Sie hier die Bildbeschreibung ein

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

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

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

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



        return [Reject, Approve]
    }

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

    func ApproveFunc(indexPath: IndexPath) {
        print(indexPath.row)
    }
    func rejectFunc(indexPath: IndexPath) {
        print(indexPath.row)
    }
Akbar Khan
quelle
Können Sie Ihrer Antwort eine Erklärung hinzufügen, damit ein Leser daraus lernen kann?
Nico Haase
Vielen Dank für dieses Code-Snippet, das möglicherweise nur begrenzte und sofortige Hilfe bietet. Eine richtige Erklärung würde ihren langfristigen Wert erheblich verbessern, indem sie zeigt, warum dies eine gute Lösung für das Problem ist, und es für zukünftige Leser mit anderen, ähnlichen Fragen nützlicher machen. Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, einschließlich der von Ihnen getroffenen Annahmen.
Tim Diekmann
1

Hier ist eine etwas fragile Methode, die keine privaten APIs oder den Aufbau eines eigenen Systems umfasst. Sie sichern Ihre Wetten ab, dass Apple dies nicht bricht und dass hoffentlich eine API veröffentlicht wird, durch die Sie diese wenigen Codezeilen ersetzen können.

  1. KVO self.contentView.superview.layer.sublayer. Tun Sie dies in init. Dies ist die Ebene von UIScrollView. Sie können keine KVO-Unteransichten erstellen.
  2. Wenn sich Unteransichten ändern, finden Sie die Bestätigungsansicht zum Löschen in scrollview.subviews. Dies erfolgt im Rückruf beobachten.
  3. Verdoppeln Sie die Größe dieser Ansicht und fügen Sie links von ihrer einzigen Unteransicht einen UIButton hinzu. Dies geschieht auch im Beobachtungsrückruf. Die einzige Unteransicht der Löschbestätigungsansicht ist die Schaltfläche Löschen.
  4. (optional) Das UIButton-Ereignis sollte self.superview nachschlagen, bis es eine UITableView findet, und dann eine von Ihnen erstellte Datenquelle oder Delegatmethode aufrufen, z. B. tableView: commitCustomEditingStyle: forRowAtIndexPath:. Sie können den Indexpfad der Zelle mithilfe von [tableView indexPathForCell: self] ermitteln.

Dies erfordert auch, dass Sie die Standard-Rückrufe für die Bearbeitung von Delegaten in der Tabellenansicht 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
xtravar
quelle
Ich bin wirklich froh, wenn Sie Beispielcode bereitstellen können. Danke
Guy Kahlon
Getan. Es könnte ein oder zwei Fehler geben, aber Sie verstehen das Wesentliche.
Xtravar
1

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

Andrey Chernukha
quelle
Ich habe es ausprobiert SwipeCellKitund war beeindruckt ... bis ich eine dieser Ausnahmen bekam, weil die Anzahl der Zeilen vor einem Update der Tabellenansicht nicht die gleiche war wie nach dem Update +/- der Änderung der Zeilen. Die Sache ist, ich habe meinen Datensatz nie geändert. Wenn das nicht besorgniserregend ist, weiß ich nicht, was es ist. Also habe ich mich gegen die Verwendung entschieden und nur die neuen UITableViewDelegate-Methoden verwendet. Wenn Sie weitere Anpassungen benötigen, können Sie jederzeit überschreibenwillBeginEditingRowAt: ....
Hufeisen7
@ Horseshoe7 das ist komisch. Ich bin bei der Verwendung von SwipeCellKit nie auf eine Ausnahme gestoßen. Welche Beziehung kann eine Zelle zu einer solchen Ausnahme haben, die aufgrund von Datenquellenänderungen auftritt?
Andrey Chernukha
1

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
}
Vini App
quelle
Was ist die Quellansicht in Ihren Codes? ist es Symbol oder Bild?
Saeed Rahmatolahi
1
@SaeedRahmatolahi sourceViewist "Die Ansicht, in der die Aktion angezeigt wurde." Weitere Informationen finden Sie unter "UIContextualAction.Handler".
Mark Moeykens
0

Hier ist eine einfache Lösung. Es ist in der Lage, benutzerdefinierte UIView in UITableViewCell anzuzeigen und auszublenden. Die Anzeigelogik ist in einer Klasse enthalten, die von UITableViewCell, BaseTableViewCell erweitert wurde.

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 Tabellenansichtszelle von BaseTableViewCell.

Als Nächstes erstellt Inside UIViewController, das UITableViewDelegate implementiert, zwei Gestenerkenner für linke und rechte Wischbewegungen.

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

In cellForRowAtIndexPath von UITableViewDelegate können Sie jetzt eine 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;
}

Diese Art des Ladens von benutzerdefiniertem UIView ist natürlich nur für dieses Beispiel. Verwalten Sie es wie Sie wollen.

Slobodans
quelle