Full swipe UITableViewCell to delete UITableView iOS 8

左心房为你撑大大i 提交于 2019-12-20 10:28:03

问题


I'd like to mimic the swipe to delete function of a UITableViewCell just like the mail app in iOS 8. I'm not referring to swipe to reveal a delete button. I'm referring to when you swipe, it discoloses 3 actions, but if you keep swiping to the left, the email is deleted.

In iOS 8, UITableView has a new method where you can provide the data to display any number of buttons:

#ifdef __IPHONE_8_0
- (NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewRowAction *viewStackRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Stack" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
        SM_LOG_DEBUG(@"View Stack Action");
    }];
    viewStackRowAction.backgroundColor = [UIColor radiusBlueColor];

    UITableViewRowAction *viewUserRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"User" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
        SM_LOG_DEBUG(@"View User Action");
    }];
    viewUserRowAction.backgroundColor = [UIColor radiusLightBlueColor];

    UITableViewRowAction *deleteRowAction = [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault title:@"Delete" handler:^(UITableViewRowAction *action, NSIndexPath *indexPath) {
        SM_LOG_DEBUG(@"Delete");
    }];
    deleteRowAction.backgroundColor = [UIColor redColor];


    return @[deleteRowAction, viewUserRowAction, viewStackRowAction];
}
#endif

I don't see any API to detect if you keep swiping though. I've grepped for 8_0 in UITableView.h and the above method seems to be the only new one.

I suppose one could monitor the scroll view offset, or add/hijack a UIPanGestureRecognizer. I just wanted to make sure to use the default way, if there is one (and get the animation for "free")


回答1:


With Swift 4.2 and iOS 12, according to your needs, you can choose one of the 3 following ways in order to create a trailing swipe action that will delete the selected UITableViewCell.


#1. Using UITableViewDataSource's tableView(_:commit:forRowAt:)

When you use tableView(_:commit:forRowAt:) with an editingStyle of value UITableViewCell.EditingStyle.delete, full swipe to delete is automatically supported by the system.

import UIKit

class TableViewController: UITableViewController {

    var numbers = [Int](0..<10)

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return numbers.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = "\(numbers[indexPath.row])"
        return cell
    }

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if (editingStyle == UITableViewCell.EditingStyle.delete) {
            self.numbers.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)
        }
    }

}

#2. Using UITableViewDelegate's tableView(_:editActionsForRowAt:) and UITableViewRowAction

In order to support full swipe to delete with UITableViewRowAction, you have to initialize it with a style that has a value of UITableViewRowAction.Style.destructive.

import UIKit

class TableViewController: UITableViewController {

    var numbers = [Int](0..<10)

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return numbers.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = "\(numbers[indexPath.row])"
        return cell
    }

    override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        // Intentionally blank in order to be able to use UITableViewRowActions
    }

    override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
        let deleteHandler: (UITableViewRowAction, IndexPath) -> Void = { _, indexPath in
            self.numbers.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)
        }
        let deleteAction = UITableViewRowAction(style: UITableViewRowAction.Style.destructive, title: "Delete", handler: deleteHandler)
        // Add more actions here if required
        return [deleteAction]
    }

}

#3. Using UITableViewDelegate's tableView(_:trailingSwipeActionsConfigurationForRowAt:) and UISwipeActionsConfiguration (requires iOS 11)

UISwipeActionsConfiguration has a property called performsFirstActionWithFullSwipe. performsFirstActionWithFullSwipe has the following declaration:

var performsFirstActionWithFullSwipe: Bool { get set }

A Boolean value indicating whether a full swipe automatically performs the first action. [...] When this property is set to true, a full swipe in the row performs the first action listed in the actions property. The default value of this property is true.

The following UITableViewController implementation show how to use UISwipeActionsConfiguration in order to manage full swipe to delete actions.

import UIKit

class TableViewController: UITableViewController {

    var numbers = [Int](0..<10)

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return numbers.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = "\(numbers[indexPath.row])"
        return cell
    }

    override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let handler: UIContextualAction.Handler = { (action: UIContextualAction, view: UIView, completionHandler: ((Bool) -> Void)) in
            self.numbers.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)
            completionHandler(true)
        }
        let deleteAction = UIContextualAction(style: UIContextualAction.Style.destructive, title: "Delete", handler: handler)
        // Add more actions here if required
        let configuration = UISwipeActionsConfiguration(actions: [deleteAction])
        configuration.performsFirstActionWithFullSwipe = true
        return configuration
    }

}



回答2:


add ui gustere recognizer to each cell, check the amount of "swipness", if its above specific threshold, do the deletion.

somthing like:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        static NSString *CellIdentifier = @"identifier";

        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
        if (cell == nil) {
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]];
        }

        UISwipeGestureRecognizer* swipe_gesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeLeft:)];
        [swipe_gesture setDirection:UISwipeGestureRecognizerDirectionLeft];
        [cell addGestureRecognizer:swipe_gesture];


        return cell;
    }

- (void)swipeLeft:(UIGestureRecognizer *)gestureRecognizer {
    int threshold = 100;
    if (sender.state == UIGestureRecognizerStateBegan) 
    {
        startLocation = [sender locationInView:self.view];
    }
    else if (sender.state == UIGestureRecognizerStateEnded) 
    {
        CGPoint stopLocation = [sender locationInView:self.view];
        CGFloat dx = stopLocation.x - startLocation.x;
        CGFloat dy = stopLocation.y - startLocation.y;
        CGFloat distance = sqrt(dx*dx + dy*dy );
        if (distance > threshold )
        {
            NSLog(@"DELETE_ROW");
        }

    }
}



回答3:


Your table view's data source has to implement

-tableView:commitEditingStyle:forRowAtIndexPath:

otherwise the built-in iOS 8 swiping functionality will not work.

This seems counterintuitive since a UITableViewRowAction accepts a block. But it's the only way I've been able to get it to work.




回答4:


You can use MGSwipeTableCell. They have implement this feature to fire callback swipeTableCell:tappedButtonAtIndex:direction:fromExpansion: with tappedButtonAtIndex equal to 0 (so it gets executed what you implemented on first added button).



来源:https://stackoverflow.com/questions/26002751/full-swipe-uitableviewcell-to-delete-uitableview-ios-8

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!