Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITableView invoke swipe actions programmatically

I have a UITableView where the user can swipe left to reveal actions (like in iOS 8 mail). That all works as expected. I want to trigger this when the user taps on a certain part of the cell. How can I invoke this slide action programmatically?

Current behavior: User must swipe the cell left to disclose the action buttons.

Desired behavior: User taps (an actions button) on the cell. Cell slides over to disclose the action buttons.

like image 662
VaporwareWolf Avatar asked Nov 05 '14 20:11

VaporwareWolf


4 Answers

Well I couldn't find a way to do this programmatically, but I came up with this workaround. When the user taps the cell, I animated (pan) it to the left to momentarily reveal afake "Swipe Me" button. This is quickly reversed so the cell is back to normal. This provides a visual cue to let the user know that they can swipe the cell:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    __block UILabel *swipeLabel = [[UILabel alloc]initWithFrame:CGRectMake(cell.bounds.size.width,
                                                                   0,
                                                                   200,
                                                                   cell.bounds.size.height)];

    swipeLabel.text = @"  Swipe Me";
    swipeLabel.backgroundColor = [UIColor greenColor];
    swipeLabel.textColor = [UIColor whiteColor];
    [cell addSubview:swipeLabel];

    [UIView animateWithDuration:0.3 animations:^{
        [cell setFrame:CGRectMake(cell.frame.origin.x - 100, cell.frame.origin.y, cell.bounds.size.width, cell.bounds.size.height)];
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:0.3 animations:^{
            [cell setFrame:CGRectMake(cell.frame.origin.x + 100, cell.frame.origin.y, cell.bounds.size.width, cell.bounds.size.height)];
        } completion:^(BOOL finished) {
            [swipeLabel removeFromSuperview];
            swipeLabel = nil;
        }];
    }];
}

Hope this helps someone.

Note that you need to set your tableViewCell's selection type to none. Else the gray bar will obscure it.

Update. I thought I'd post a more Swifty version:

func previewActions(forCellAt indexPath: IndexPath) {
    guard let cell = tableView.cellForRow(at: indexPath) else {
        return
    }

    let label: UILabel = {
        let label = UILabel(frame: CGRect.zero)
        label.text = "  Swipe Me  "
        label.backgroundColor = .blue
        label.textColor = .white
        return label
    }()

    // Figure out the best width and update label.frame
    let bestSize = label.sizeThatFits(label.frame.size)
    label.frame = CGRect(x: cell.bounds.width - bestSize.width, y: 0, width: bestSize.width, height: cell.bounds.height)
    cell.insertSubview(label, belowSubview: cell.contentView)

    UIView.animate(withDuration: 0.3, animations: {
        cell.transform = CGAffineTransform.identity.translatedBy(x: -label.bounds.width, y: 0)
        label.transform = CGAffineTransform.identity.translatedBy(x: label.bounds.width, y: 0)
    }) { (finished) in
        UIView.animateKeyframes(withDuration: 0.3, delay: 0.25, options: [], animations: {
            cell.transform = CGAffineTransform.identity
            label.transform = CGAffineTransform.identity
        }, completion: { (finished) in
            label.removeFromSuperview()
        })
    }
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    previewActions(forCellAt: indexPath)
    return
}
like image 105
VaporwareWolf Avatar answered Sep 22 '22 19:09

VaporwareWolf


For anyone in search of the Swift version of VaporwareWolf's answer, here it is:

func animateRevealHideActionForRow(tableView: UITableView, indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath)

    // Should be used in a block
    var swipeLabel: UILabel? = UILabel.init(frame: CGRect(x: cell!.bounds.size.width,
                                                          y: 0,
                                                          width: 200,
                                                          height: cell!.bounds.size.height))

    swipeLabel!.text = "  Swipe Me";
    swipeLabel!.backgroundColor = UIColor.init(red: 255/255, green: 41/255, blue: 53/255, alpha: 1) // Red
    swipeLabel!.textColor = UIColor.white
    cell!.addSubview(swipeLabel!)

    UIView.animate(withDuration: 0.3, animations: {
        cell!.frame = CGRect(x: cell!.frame.origin.x - 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width + 100, height: cell!.bounds.size.height)
    }) { (finished) in
        UIView.animate(withDuration: 0.3, animations: {

            cell!.frame = CGRect(x: cell!.frame.origin.x + 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width - 100, height: cell!.bounds.size.height)

        }, completion: { (finished) in
            swipeLabel?.removeFromSuperview()
            swipeLabel = nil;
        })
    }
}
like image 43
Burak Avatar answered Sep 23 '22 19:09

Burak


Don't go with a hacky way and use a library like this until they implement the feature.

https://github.com/SwipeCellKit/SwipeCellKit

This library let you do things like: cell.showSwipe(orientation: .right, animated: true)

like image 41
Izumi.H Avatar answered Sep 23 '22 19:09

Izumi.H


And for a Swift 3 version where you want to call this function for every row.

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

    let cell = tableView.cellForRow(at: indexPath);
    UIView.animate(withDuration: 0.3, animations: {

        cell!.frame = CGRect(x: cell!.frame.origin.x - 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width + 100, height: cell!.bounds.size.height)

    }) { (finished) in
        UIView.animate(withDuration: 0.3, animations: {

            cell!.frame = CGRect(x: cell!.frame.origin.x + 100, y: cell!.frame.origin.y, width: cell!.bounds.size.width - 100, height: cell!.bounds.size.height)

        }, completion: { (finished) in
        })
    }
}
like image 23
Mikael Nyborg Avatar answered Sep 23 '22 19:09

Mikael Nyborg